问题
Angular 2,4 formbuilder setvalue() is not working as expected when used on dropdown selects.
I have the following dropdown select that gets populated with Github organizations:
<select name="org" formControlName="organizations">
<option *ngFor="let org of organizations" [ngValue]="org">{{org.organization.login}}</option>
</select>
Here is the javascript code that sets the Github organization that should be selected.
this.pipelineForm = fb.group({
name:'',
organizations:'',
repos: '',
branches:'',
runtime:'',
versions:''
});
userService.getOrganizations().subscribe((org:github)=>{
let organizationName = org.data[0];
this.organizations = org.data;
this.projects.subscribe((p)=> {
p[1].project.subscribe((f)=>{
this.pipelineForm.get("organizations").setValue(f.organizations, {onlySelf: true});
//this.pipelineForm.patchValue(f);
});
});
});
I expect the corresponding dropdown option to be selected when I pass the value to the setValue()
. Instead, I get a blank option. I also tried with patchValue()
. No luck.
回答1:
I struggled with the same problem and found Igor's answer. It was too vague so I dug around more. I finally figured it out, and Igor's answer is correct albeit lacking in detail. Hopefully this will help anyone else in the future trying to get setValue()
on dropdowns and object models to work happily together.
Generally, the object model passed into setValue()
should be the same as the objects you inject into your dropdown selector. E.g. if of type Organization
, then the setValue
should be also be of type Organization
. It's not necessary, however, to share completely identical properties.
To get the setValue()
or patchValue()
to work with an object model (as opposed to some primitive type), use the [compareWith]
function as Igor pointed out.
From node_modules/@angular/material/select/typings/select.d.ts
:
compareWith is a function to compare the option values with the selected values. The first argument is a value from an option. The second is a value from the selection. A boolean should be returned.
In your html template,
<select [compareWith]="compareOrgs" name="org" formControlName="organizations">
<option *ngFor="let org of organizations" [ngValue]="org">{{org.organization.login}}</option>
</select>
And in your component.ts
file, define the compareOrgs()
function:
compareOrgs(c1: any, c2: any): boolean {
return c1 && c2 ? c1.orgId === c2.orgId : c1 === c2;
}
Don't forget to call the setValue()
function, for e.g. in ngOnInit()
or in the callback function if it's async and you're fetching data.
E.g. in the callback from OP's code above,
// "organization" is whatever object you're trying to patch on the dropdown
this.pipelineForm.get("organizations").setValue(organization)
So what all this does is, the compareOrgs
function will compare the value of the selected value (which is the object you passed into setValue
, now labelled as c2
in compareOrgs
) with each of the option values (c1
). Specifically, it compares the orgId
properties of the selected and option values. This is how the FormControl
knows which value to pre-select on the dropdown.
You can now access the object with [ngValue]
.
This is a good resource that helped me: https://codeburst.io/angular-material-select-module-the-comparewith-function-9dfdb4035373
回答2:
A little example, we want to save a number. see [ngValue]="org.id" and this.myForm.controls['organization'].setValue(value);
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
interface Iorganization {
id: number;
name: string;
}
@Component({
selector: 'app-select',
template: `
<form [formGroup]="myForm" novalidate>
<select name="org" formControlName="organization">
<option *ngFor="let org of organizations" [ngValue]="org.id">{{org.name}}</option>
</select>
</form>
<button (click)="setOrg(1)">Org 1</button>
<button (click)="setOrg(2)">Org 2</button>
{{myForm.value |json}}
`,
styleUrls: ['./select.component.css']
})
export class SelectComponent implements OnInit {
organizations: Iorganization[];
myForm: FormGroup;
constructor(private fb: FormBuilder) { }
ngOnInit() {
this.organizations = [
{ id: 1, name: "org 1" },
{ id: 2, name: "org 2" },
{ id: 3, name: "org 3" },
{ id: 4, name: "org 4" }
];
this.myForm = this.fb.group({
organization: 1
})
}
setOrg(value: number) {
this.myForm.controls['organization'].setValue(value);
}
}
if we want to save a object -see {{myForm.value |json}}- we need make a few changes. see that we put [ngValue]="org", but the setOrg() function use a object to make setValue. And not, it's not valid setValue({id:1,name:'org1'})
import { Component, OnInit } from '@angular/core';
....
template: `
<form [formGroup]="myForm" novalidate>
<select name="org" formControlName="organization">
<option *ngFor="let org of organizations" [ngValue]="org">{{org.name}}</option>
</select>
....`
export class SelectComponent implements OnInit {
...
ngOnInit() {
this.organizations = [
{ id: 1, name: "org 1" },
....
];
this.myForm = this.fb.group({
organization: null
})
}
setOrg(value: number) {
let organization=this.organizations.find(o=>o.id==value);
this.myForm.controls['organization'].setValue(organization);
}
回答3:
Use the attribute compareWith
on your select
tag:
<select ... [compareWith]="compareByName">
Then implement a simple function in your component
compareByName(org1: Org, org2: Org): boolean {
return org1 && org2 ? org1.name === org2.name : org1 === org2;
}
来源:https://stackoverflow.com/questions/46780829/formbuilder-setvalue-is-not-working-as-expected-when-used-on-dropdown-selects