Angular 响应式表单:动态 Select 下拉值不具有约束力
Angular Reactive Forms: Dynamic Select dropdown value not binding
我有两个数据数组:AssociatedPrincipals(以前保存的数据)和 ReferencePrincipals(要填充到下拉控件中的静态数据)。我正在努力从 AssociatedPrincipals 获取先前的值 displayed/selected 在 动态数量 (大多数示例使用单个下拉列表)页面加载的下拉列表中。
我不确定如何设置表单(代码隐藏和 HTML),尤其是设置 Select 的 formControlName。目前,每个下拉列表中的静态值都会填充,但我无法正确绑定选择的值。
public ngOnInit() {
this.factsForm = this.formbuilder.group({
associatedPrincipals: this.formbuilder.array([]),
referencePrincipals: this.formbuilder.array([])
});
// Data for both of these methods comes from external source...
var responseData = // HTTP source...
// Push retrieved data into form
this.initPrincipals(responseData[0]);
// Push static data into form
this.initStaticData(responseData[1]);
}
public initPrincipals(principals?: IAssociatedPrincipal[]): FormArray {
principals.forEach((principal) => {
this.associatedPrincipals.push(this.createPrincipalFormGroup(principal));
});
}
public initStaticData(response: IReferencePrincipal[]) {
response.forEach((principal) => {
this.referencePrincipals.push(
this.formbuilder.control({
code: principal.code,
canHaveLead: principal.canHaveLead,
isDuplicate: false
}));
});
}
public createPrincipalFormGroup(principal: IAssociatedPrincipal) {
return this.formbuilder.group({
code: principal.code,
canHaveLead: false,
isDuplicate: false
});
}
public get associatedPrincipals(): FormArray {
return this.factsForm.get('associatedPrincipals') as FormArray;
}
public get referencePrincipals(): FormArray {
return this.factsForm.get("referencePrincipals") as FormArray;
}
HTML:
<form novalidate [formGroup]="factsForm">
<div formArrayName="associatedPrincipals">
<div *ngFor="let associatedPrincipal of associatedPrincipals.controls; let i=index;" [formGroupName]="i" >
<select class="form-control create-input"
formControlName="i">
<option value=null disabled selected hidden>--Select--</option>
<option *ngFor="let refPrincipal of referencePrincipals.controls" [ngValue]="refPrincipal">refPrincipal.value.code</option>
</select>
</div>
</div>
</form>
感谢任何反馈!
编辑:添加了显示问题的 Plunker: https://embed.plnkr.co/XMLvFUbuc32EStLylDGO/
您需要将填充 select 的对象的完全相同的引用传递给 selected 对象以获得 selected 值。
此处您使用 referencePrincipals
中所有 FormControl
的值来填充您的 selectbox
,因此要获得它 selected 使用此对象:
public createPrincipalFormControl(principal) {
const selectedFormControl = this.referencePrincipals.controls.find(form => form.value.code === principal.code)
return this.formbuilder.control(selectedFormControl.value);
}
正在工作的 plunker。 https://plnkr.co/edit/vw3WZ6?p=preview
您的方法至少有 2 个问题。
你这里的数据源可能是异步的。这意味着你不应该在 var responseData
之后立即执行 this.initiPrincipals(responseData[0])
,而是在任何方法的回调中获取数据,或者在订阅 http 服务时,如果你通过 Observable 获取数据。
let subscription = myservice.getmedata.subscribe(data =>
{
//here you should do your initializations with data from server
};
如果你的数据来自@Input()那么正确的请是ngOnChanges
.
正如 Fetra 指出的那样,无论您之前 selected 选项的值与您预先填充到 select 列表中的选项的值完全相同,在为了将其设置为 selected,您需要准确参考您填充它的内容。所以,像这样:
this.formGroup.controls['yourSelectControl'].patchValue(this.yourInitialCollectionOfOptions.find(v => v.propertyByWhichYouWantToCompare == valueFromServer.propertyByWhichYouWantToCompare)
演示中的问题
根据您提供的demo,存在以下几个问题:
- 没有
formControlName
分配给 select
。
- 您正在将 object 绑定到 select 的选项。
第一个问题
因为您正在循环 associatedPrincipals
以动态显示下拉列表。 associatedPrincipals
是一个 formArray 可以考虑如下:
associatedPrincipals = {
"0": FormControl,
"1": FormControl
}
所以你可以简单地将 *ngFor
表达式中定义的 i
分配给 formControlName
.
<select formControlName="{{i}}" style="margin-top: 10px">
...
</select>
第二题
虽然将 object 绑定到 option
,Angular 将通过 object 比较默认值和 option
的值默认情况下 。
您可以将相同的实例(从 referencePrincipals
的 formControls 的值获取)设置为 associatedPrincipals
的 formControl(如@Fetra R. 的回答)。但这不是最方便的方法,因为您必须采用一些逻辑来保持对象的相同实例。
这里我会给你另一个解决方案,它使用专门针对你当前情况设计的compareWith
指令,参见docs。
使用compareWith
指令,你只需要实现一个compareFun
来告诉angular如何考虑两个对象(具有不同的实例)作为same.Here你可以同时包含 comparing object instance
和 comparing object fields
。
<select formControlName="{{i}}" style="margin-top: 10px" [compareWith]="compareFun">
<option value=null disabled selected hidden>--Select--</option>
<option *ngFor="let refPrincipal of referencePrincipals.controls"
[ngValue]="refPrincipal.value">{{ refPrincipal.value.code }}</option>
</select>
// tell angular how to compare two objects
compareFn(item1, item2): boolean {
return item1 && item2 ? item1.code === item2.code : item1 === item2;
}
我有两个数据数组:AssociatedPrincipals(以前保存的数据)和 ReferencePrincipals(要填充到下拉控件中的静态数据)。我正在努力从 AssociatedPrincipals 获取先前的值 displayed/selected 在 动态数量 (大多数示例使用单个下拉列表)页面加载的下拉列表中。
我不确定如何设置表单(代码隐藏和 HTML),尤其是设置 Select 的 formControlName。目前,每个下拉列表中的静态值都会填充,但我无法正确绑定选择的值。
public ngOnInit() {
this.factsForm = this.formbuilder.group({
associatedPrincipals: this.formbuilder.array([]),
referencePrincipals: this.formbuilder.array([])
});
// Data for both of these methods comes from external source...
var responseData = // HTTP source...
// Push retrieved data into form
this.initPrincipals(responseData[0]);
// Push static data into form
this.initStaticData(responseData[1]);
}
public initPrincipals(principals?: IAssociatedPrincipal[]): FormArray {
principals.forEach((principal) => {
this.associatedPrincipals.push(this.createPrincipalFormGroup(principal));
});
}
public initStaticData(response: IReferencePrincipal[]) {
response.forEach((principal) => {
this.referencePrincipals.push(
this.formbuilder.control({
code: principal.code,
canHaveLead: principal.canHaveLead,
isDuplicate: false
}));
});
}
public createPrincipalFormGroup(principal: IAssociatedPrincipal) {
return this.formbuilder.group({
code: principal.code,
canHaveLead: false,
isDuplicate: false
});
}
public get associatedPrincipals(): FormArray {
return this.factsForm.get('associatedPrincipals') as FormArray;
}
public get referencePrincipals(): FormArray {
return this.factsForm.get("referencePrincipals") as FormArray;
}
HTML:
<form novalidate [formGroup]="factsForm">
<div formArrayName="associatedPrincipals">
<div *ngFor="let associatedPrincipal of associatedPrincipals.controls; let i=index;" [formGroupName]="i" >
<select class="form-control create-input"
formControlName="i">
<option value=null disabled selected hidden>--Select--</option>
<option *ngFor="let refPrincipal of referencePrincipals.controls" [ngValue]="refPrincipal">refPrincipal.value.code</option>
</select>
</div>
</div>
</form>
感谢任何反馈!
编辑:添加了显示问题的 Plunker: https://embed.plnkr.co/XMLvFUbuc32EStLylDGO/
您需要将填充 select 的对象的完全相同的引用传递给 selected 对象以获得 selected 值。
此处您使用 referencePrincipals
中所有 FormControl
的值来填充您的 selectbox
,因此要获得它 selected 使用此对象:
public createPrincipalFormControl(principal) {
const selectedFormControl = this.referencePrincipals.controls.find(form => form.value.code === principal.code)
return this.formbuilder.control(selectedFormControl.value);
}
正在工作的 plunker。 https://plnkr.co/edit/vw3WZ6?p=preview
您的方法至少有 2 个问题。
你这里的数据源可能是异步的。这意味着你不应该在
var responseData
之后立即执行this.initiPrincipals(responseData[0])
,而是在任何方法的回调中获取数据,或者在订阅 http 服务时,如果你通过 Observable 获取数据。let subscription = myservice.getmedata.subscribe(data => { //here you should do your initializations with data from server };
如果你的数据来自@Input()那么正确的请是
ngOnChanges
.正如 Fetra 指出的那样,无论您之前 selected 选项的值与您预先填充到 select 列表中的选项的值完全相同,在为了将其设置为 selected,您需要准确参考您填充它的内容。所以,像这样:
this.formGroup.controls['yourSelectControl'].patchValue(this.yourInitialCollectionOfOptions.find(v => v.propertyByWhichYouWantToCompare == valueFromServer.propertyByWhichYouWantToCompare)
演示中的问题
根据您提供的demo,存在以下几个问题:
- 没有
formControlName
分配给select
。 - 您正在将 object 绑定到 select 的选项。
第一个问题
因为您正在循环 associatedPrincipals
以动态显示下拉列表。 associatedPrincipals
是一个 formArray 可以考虑如下:
associatedPrincipals = {
"0": FormControl,
"1": FormControl
}
所以你可以简单地将 *ngFor
表达式中定义的 i
分配给 formControlName
.
<select formControlName="{{i}}" style="margin-top: 10px">
...
</select>
第二题
虽然将 object 绑定到 option
,Angular 将通过 object 比较默认值和 option
的值默认情况下 。
您可以将相同的实例(从 referencePrincipals
的 formControls 的值获取)设置为 associatedPrincipals
的 formControl(如@Fetra R. 的回答)。但这不是最方便的方法,因为您必须采用一些逻辑来保持对象的相同实例。
这里我会给你另一个解决方案,它使用专门针对你当前情况设计的compareWith
指令,参见docs。
使用compareWith
指令,你只需要实现一个compareFun
来告诉angular如何考虑两个对象(具有不同的实例)作为same.Here你可以同时包含 comparing object instance
和 comparing object fields
。
<select formControlName="{{i}}" style="margin-top: 10px" [compareWith]="compareFun">
<option value=null disabled selected hidden>--Select--</option>
<option *ngFor="let refPrincipal of referencePrincipals.controls"
[ngValue]="refPrincipal.value">{{ refPrincipal.value.code }}</option>
</select>
// tell angular how to compare two objects
compareFn(item1, item2): boolean {
return item1 && item2 ? item1.code === item2.code : item1 === item2;
}