Matt
Matt

Reputation: 357

Setting selected option of select control in an Angular 2 model-driven form

I have researched many similar existing answers on SO and elsewhere, but just can't find the solution to this.

I'm using the model-driven approach in Angular 2 to build my form, which is both an add and edit form. When in edit mode, the values are populated with data retrieved from a service: this aspect is all fine because the simple text inputs all bind correctly.

One of the properties is 'Country' and this is an object as follows:

    export class Country {id: number; name: string;}

I want to bind this to a select control which will have the list of countries available, and the one from the model populated when the form loads. I want the value of the binding to be the country object, not just the id.

Here's the html of the select control:

    <select class="form-control" id="country" formControlName="country">
          <option value="default">--Select a country--</option>
          <option *ngFor="let c of countries" [value]="c">{{c.name}}      </option>
</select> 

And here is where i try to to populate the value from the component class:

    (<FormControl>this.personForm.controls['country'])
 .setValue(this.person.country, { onlySelf: true });

But there is no selected option when the page loads, even though the console confirms that this.person.country exists and is populated with the correct object.

I can get it working with ids: changing to [value]="c.id" in the view and appending .id in the class, and then it works in that the right option is selected. The problem is that the select no longer emits an object for the country property, just the id. I tried changing [value] to [ngValue] and get the same result. I even added [ngModel]="country" to the select element and that didn't help either.

I'd be grateful for any help.

Upvotes: 16

Views: 70489

Answers (1)

silentsod
silentsod

Reputation: 8335

The issue is most likely that this.person.country is not the same country as in your countries array.

If we want to make them the same we can either explicitly subscribe to the valueChanges of the select control or bind [(ngModel)] to person.country:

subscribe to changes

code

this.countryForm.controls['country'].valueChanges.subscribe(country => 
  this.person.country = country;
);

// initialize by finding the correct country object (this will overwrite the person's country object)
this.countryForm.controls['country'].setValue(countries.filter(c => c.id === person.country.id));

template

ngModel bind

We still have to make the objects match (compare strategy that Angular 2 uses which is really what JS uses)

code

this.person.country = this.countries.filter(c => c.id === this.person.country.id)[0];

template

<select class="form-control" id="country" formControlName="country" [(ngModel)]="person.country">
    <option value="default">--Select a country--</option>
    <option *ngFor="let c of countries" [ngValue]="c">{{c.name}}</option>
</select>

ngModel Plunker: http://plnkr.co/edit/UIS2V5rKh77n4JsjZtii?p=preview

subscription Plunker: http://plnkr.co/edit/yyZ6ol1NPD77nyuzwS2t?p=info

Upvotes: 16

Related Questions