Angular 5: ngModel binding on select element doesn't update - html

I have this markup for a select element:
<select type="submit" ([ngModel])="selectedValue" #item (change)="onSelectItem(item.value)">
<option>Pick an option</option>
<option *ngFor="let object of objects">{{ object.value }}</option>
</select>
When I update the ngModel binding from typescript, nothing happens. Essentially I am just doing this, in the component:
ngOnInit() {
this.selectedValue = 'something' // One of the options from the list of objects
}
However, nothing happens.
The value I am trying to update it to, are in the list of objects (in the *ngFor) - if that matters.

Change ([ngModel])="selectedValue" to [(ngModel)]="selectedValue"
Just like the docs say:
Visualize a banana in a box to remember that the parentheses go inside the brackets.
Also you do not need the (change) listener if you are using ngModel. You can split your two way binding into [ngModel] and (ngModelChange)
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `
<select type="submit" [ngModel]="selectedValue" (ngModelChange)="onSelectedChange($event)">
<option *ngFor="let option of options">{{ option }}</option>
</select>
{{ selectedValue }}
` ,
styleUrls: ['./app.component.css']
})
export class AppComponent {
selectedValue = 1;
options = [1,2,3]
onSelectedChange(value: number) {
// do something else with the value
console.log(value);
// remember to update the selectedValue
this.selectedValue = value;
}
}
Live demo

Please change the definition of ([ngModel]) to [(ngModel)] and you should initialize the objects values before assign the value in selectedValue object

Related

Change the value of checkbox to string

I already saw many questions but I still can't change the value of a checkbox to a string. I need to change the value according to true - false -> 'A' - 'B'. I am using reactive forms in angular.
I got this:
...
..*ngFor="let item of myForm.get('people')['controls'];
...
<mat-checkbox formControlName="check"
[checked]="item.get('check').value === 'A' ? true : false"
(change)="item.get('check').setValue($event.checked ? 'A' : 'B')">
</mat-checkbox>
It has to be in check true if the value that comes is 'A' and in false if it is 'B', I really don't see whydon't set that value on change. I need to send as a string the value of the checkbox
<form [formGroup]="form" (ngSubmit)="submit()">
{{form.value | json}} // just for to see the output
<ng-container formArrayName="people" *ngFor="let item of people.controls; let i = index">
<div [formGroupName]="i">
<mat-checkbox formControlName="check" [checked]="item.get('check').value === 'A' ? true : false"
(change)="setCheck($event.checked, i)">
</mat-checkbox>
</div>
</ng-container>
<button mat-button>submit</button>
</form>
import { FormArray } from '#angular/forms';
import { Component, OnInit } from "#angular/core";
import { FormBuilder, FormGroup } from '#angular/forms';
#Component({
selector: "app-test",
templateUrl: "./test.component.html",
styleUrls: ["./test.component.scss"],
})
export class TestComponent implements OnInit {
form: FormGroup
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.createForm();
}
createForm(){
this.form = this.formBuilder.group({
people: this.formBuilder.array([
this.createItem()
])
})
}
createItem(){
return this.formBuilder.group({
check: ''
})
}
addItem(){
this.people.push(this.createItem());
}
get people(){
return this.form.get("people") as FormArray;
}
setCheck(checked, index){
this.people.controls[index].get("check").setValue(checked ? 'A' : 'B');
}
submit(){
console.log(this.form.value);
}
}
and one more thing, try to use presentation logic in the component class.
style guide: Style 05-17.
link: https://angular.io/guide/styleguide#put-presentation-logic-in-the-component-class
First, if you create many many checkboxes inside a loop, and you set formControlName to a specific one, every checkbox will be bind to 1 control. (You will check and uncheck all of them with the same click.)
On second, you should use formControlName, in formGroup:
<div [formGroup]="myForm">
...
..*ngFor="let item of myForm.get('people')['controls'];"
...
<mat-checkbox formControlName="check"
[checked]="item.get('check').value === 'A' ? true : false"
(change)="item.get('check').setValue($event.checked ? 'A' : 'B')">
</mat-checkbox>
</div>
On Third, I suggest to use brackets in ngFor and '' to look for control name as string.
[formControlName]="'check'"
And finally, I suggest to use mat-checkbox's (change) event, and call a custom function, and do whatever you want on ts side:
<mat-checkbox [formControlName]="'check'" (change)="someFunction(item, $event)"></mat-checkbox>
And in ts:
someFunction(item, event){
...//Do your black magic here
}
By the way: If you use reactive form, and bind a controls to a checkbox, it's value always will be boolean, cuz it's logic... You should decide to map the value later to 'A' or 'B', but you shouldn't use the same time.
And backwards: The checkbox will always work with true or false, width FormControl.
Think about it: You want a Control to control true or false value, but you force 'A' or 'B' in it... It's illogic.
Please re-think your solution.

Nothing selected in Select by default - how to fix that?

I'm using Angular 5 and I have a simple Select:
<select class="ui fluid dropdown" formControlName="device">
<option *ngFor="let device of getSenderDevices()" [ngValue]="device">{{device.Name}}</option>
</select>
My problem is the fact that by default nothing is selected, but I'd like the first option to be selected. I saw many threads like this, the solution that I thought would work, does not:
<select class="ui fluid dropdown" formControlName="device">
<option *ngFor="let device of getDevices(); let i = index" [ngValue]="device" [selected]="i==0">{{device.Name}}</option>
</select>
I also found some advices to use compareWith directive - but I wasn't able to understand how it works.
Maybe the problem is caused by getDevices(), which returns data with some delay, because the data is fetched from external server. In the beginning select has to be empty, because the data is not ready yet. When it arrives however, I'd like the select to show that by auto-selecting first option.
Not use a function getDevices() and not use [selected] in .html
//I imagine you have a serviceData that return an observable
export class DataService {
constructor(private httpClient HttpClient) {}
public getDevices():Observable<any>
{
return this.httpClient.get("....");
}
}
constructor(private fb:FormBuilder,private myservice:ServiceData){}
ngOnInit()
{
this.myService.getDevices().subscribe(res=>{
this.devices=res;
this.createForm();
})
//If getDevices() simple return an array, not from service
// this.devices=getServices();
// this.createForm();
}
createForm()
{
this.myForm=this.fb.group({device:this.device[0]})
}
<form [formGroup]="myForm">
<select class="ui fluid dropdown" formControlName="device">
<!--NOT use [selected], when "device" get the value, the select show the value-->
<!--I use the variables "devices"--->
<option *ngFor="let device of devices; let i = index"
[ngValue]="device">{{device.Name}}</option>
</select>
</form>

Angular 4 firebase dropdown select tag option

I am using Angular 4 and firebase. I'm trying to build a dropdown selector for a list of properties.
Once a property is selected from the dropdown list, the property location should appear in another input field below.
properties.component.html
<select [(ngModel)]="selectedProperty" (change)="onSelect($event, property)">
<option>--select property--</option>
<option *ngFor="let property of properties">{{property.propertyName}}</option>
</select>
<div *ngIf="selectedProperty">
<label >Location </label>
<input type="text" value="{{selectedProperty.location}}">
</div>
properties.component.ts
import { Component, OnInit } from '#angular/core';
import { PropertyService } from './../services/property.service';
import { Property } from './../models/property';
import { Observable } from 'rxjs/Observable';
#Component({
selector: 'app-property-list',
templateUrl: './property-list.component.html',
styleUrls: ['./property-list.component.scss']
})
export class PropertyListComponent implements OnInit {
properties: Observable<Property[]>;
selectedProperty: Property;
constructor(private propertyService: PropertyService) {}
ngOnInit() {
this.propertyService.getProperties().subscribe(properties => {
this.properties = properties;
})
}
onSelect(event, property: Property){
this.selectedProperty = property;
}
}
I am able to select the property from the dropdown list but the property location does not appear on the input field. I'll appreciate your help.
Instead of value use ngModel
<div *ngIf="selectedProperty">
<label >Location </label>
<input type="text" [(ngModel)]="selectedProperty.location">
</div>
By default selecting option from dropdown selects whatever value provided in selected option tag. So when you select any value in dropdown it putspropertyName inside respective ngModel.
As you want to select the whole object use ngValue in option, what that will do is, when user selects an option it will take down ngValue object value and assigned it to ngModel of select field.
<option [ngValue]="property" *ngFor="let property of properties">
{{property.propertyName}}
</option>

Value not showing for input field using one way binding angular2

Objective: Get a collection of values based on the dropdown selection and place them in hidden input fields to be included in my model;
The relative html:
<select class="selectFoo" (change)="onSelect($event.target.value)" name="FooName" ngModel>
<option selected="selected">--Select--</option>
<option *ngFor="let foo of foos" [value]="foo.ID">{{foo.Name}}
</option>
</select>
<input type="hidden" [value]="fooAddress" name="FooAddress" ngModel/>
In the code above I called a function named OnSelect to get the data about the selected foo. The foos are populated using a webservice call. Here is the snippet from my ts file.
import { Component, OnInit } from '#angular/core';
import { Foo } from './model';
import { DataService } from './data.service';
#Component({
moduleId: module.id,
selector: 'add-on',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
foos : Foo[];
selectedFoo: Foo;
fooAddress: string;
onSelect(fooID){
this.selectedFoo = null;
for(var i = 0; i < this.foos.length; i++)
{
console.log(this.foos[i].ID);
if(this.foos[i].ID == fooID){
this.selectedFoo = this.foos[i];
this.fooAddress = this.selectedFoo.Address.toString();
}
}
}
}
I originally tried one way binding my value to the selectedFoo but I was getting an error indicating my Address value wasn't defined. I noticed I could set the value equal to selectedFoo and it didn't error. So i created a new variable that was set to the fooAddress based on the selected foo. I get no value even though while stepping through the code I see it has a value.
How can I get my value to populate so I can use it in my model? Let me know if I need to provide anything else.
Thanks!
If I am correctly understanding what you are after then something like this would work:
<select name="FooName" [(ngModel)]="selectedFoo">
<option>--Select--</option>
<option *ngFor="let foo of foos" [ngValue]="foo" >{{foo.Name}}</option>
</select>
<input type="hidden" [value]="selectedFoo?.Address" name="FooAddress" />
//Assuming your 'foo' items are e.g. { ID: 1, Name: 'Hello', Address: '123 Hello St'}
Here you can bind the Address property of the selectedFoo directly to your hidden input field, rather than needing to handle the (change) event.

setting display of select in angular 2

I am trying to set the display of select tag to a value obtained through another call. But I am not able to do so even after trying [selected] ,[ngValue],[value],etc.
<select class="form-control" [(ngModel)]="selectedRegion" name="Region" (ngModelChange)="onRegionChange(selectedRegion)" val="true" id="cloud-region" >
<option *ngFor="let region of regions" [selected]="selectedRegion.Name===region.Name?'region.Name':null">{{region.Name}}</option>
</select>
I set the value of "selectedRegion" after obtaining data through a http response in an Observable. I basically want to display the region that is received in another call as the selected display of select out of a pre-decided array which also contains this value.
Tried it in this plunker. Couldn't do it.
https://plnkr.co/edit/njGlIV?p=preview
Thanks.
I believe you're missing a [value] attribute on your <option>
<select
class="form-control"
[(ngModel)]="selectedRegion" name="Region"
(ngModelChange)="onRegionChange(selectedRegion)"
val="true"
id="cloud-region">
<option
*ngFor="let region of regions"
[value]="region.Name"
[selected]="selectedRegion.Name===region.Name?'region.Name':null">{{region.Name}}</option>
</select>
I don't think selected is actually needed if you've already tied up your select element with a model. The reason why your select isn't reflecting because your options have no value.
UPDATED:
Based on your plunkr, I've added a simulated http call to retrieve a value from an Observable. Once i've received a value from the stream, I immediately updated my model.
//our root app component
import {Component, Directive, Output, EventEmitter, Input, SimpleChange} from 'angular2/core'
import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
//import 'rxjs/Rx';
#Component({
selector: 'my-app',
template:`
<h1>Selecting Number</h1>
<select type="number" [(ngModel)]="levelNum" (ngModelChange)="toNumber()">
<option *ngFor="let level of levels" [ngValue]="level.num">{{level.name}}</option>
</select>
{{levelNum}}
`,
})
export class AppComponent {
levelNum:number = 0;
levels:Array<Object> = [
{num: 0, name: "AA"},
{num: 1, name: "BB"}
];
ngOnInit(){
this.fakeHttpCall()
.subscribe(res => {
this.levelNum = res;
});
}
toNumber(){
this.levelNum = +this.levelNum;
console.log(this.levelNum);
}
fakeHttpCall(){
let request = new Observable(
obs => {
let timeout = setTimeout(() => {
obs.next(1);
obs.complete();
clearTimeout(timeout);
},2000);
}
);
return request;
}
}