javascript Angular Reactive Forms:动态选择下拉值不绑定

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46164708/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 06:30:13  来源:igfitidea点击:

Angular Reactive Forms: Dynamic Select dropdown value not binding

javascriptangularangular-reactive-forms

提问by dotNetkow

I have two arrays of data: AssociatedPrincipals (previously saved data) and ReferencePrincipals (static data to populate in dropdown controls). I'm struggling to get the previous value from AssociatedPrincipals to be displayed/selected in a dynamic amount(most examples use a single dropdown) of dropdowns on page load.

我有两个数据数组:AssociatedPrincipals(以前保存的数据)和 ReferencePrincipals(在下拉控件中填充的静态数据)。我正在努力从 AssociatedPrincipals 获取先前的值,以便在页面加载时以动态数量(大多数示例使用单个下拉列表)显示/选择下拉列表。

I'm not certain how to set up the form (code behind and HTML), especially setting the Select's formControlName. Currently, the static values in each dropdown populate, but I cannot get the selected valueto bind properly.

我不确定如何设置表单(代码隐藏和 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:

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>

I appreciate any feedback!

我感谢任何反馈!

EDIT: Added Plunker showing the issue:https://embed.plnkr.co/XMLvFUbuc32EStLylDGO/

编辑:添加了显示问题的Plunker:https ://embed.plnkr.co/XMLvFUbuc32EStLylDGO/

回答by Pengyy

Problems in your demo

演示中的问题

Based on the demo you provided, There are several problems as listed below:

根据您提供的demo,存在以下几个问题:

  • There is no formControlNameassigned to select.
  • You are binding objectto select's option.
  • 没有formControlName分配给select
  • 您正在将对象绑定到选择的选项。

For the first problem

对于第一个问题

Since you are looping through associatedPrincipalsto show dropdownlist dynamically. And associatedPrincipalswhich is a formArraywhich can consider as below:

由于您正在循环associatedPrincipals显示动态下拉列表。而且associatedPrincipals这是一个formArray可以如下考虑:

associatedPrincipals = {
  "0": FormControl,
  "1": FormControl
}

So you can simply assign iwhich is defined at *ngForexpression to formControlName.

所以你可以简单地将i*ngFor表达式中定义的赋值给formControlName

<select formControlName="{{i}}" style="margin-top: 10px">
   ...
</select>

For the second problem

对于第二个问题

While binding objectto option, Angular will compare default value and option's value by object instanceby default.

虽然绑定对象option,角将比较默认值,并option通过“价值对象实例默认。

You can set same instance(get from value of referencePrincipals's formControls) to formControl of associatedPrincipals(as @Fetra R.'s answer). But this is not the most convenient way since you have to take some logic to keep the same instance of an object.

您可以将相同的实例(从referencePrincipals的 formControls 的值中获取)设置为 formControl 的associatedPrincipals(如@Fetra R. 的回答)。但这不是最方便的方法,因为您必须采取一些逻辑来保持对象的相同实例。

Here I would give you another solution which is using compareWithdirective designed specifically for your current situation, see docs.

在这里,我将为您提供另一种解决方案,该解决方案使用compareWith专为您当前情况设计的指令,请参阅文档

Using compareWithdirective, you just need to implement a compareFunto tell angular how to consider two objects(with different instances) as the same.Here yo can include comparing object instanceand comparing object fieldsat the same time.

使用compareWith指令,你只需要实现 acompareFun来告诉 angular 如何将两个对象(具有不同的实例)视为相同的。在这里你可以同时包含comparing object instancecomparing 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;
}

Refer docsand fixed demoto learn detail about it.

请参阅文档和固定演示以了解有关它的详细信息。

回答by Fetrarij

You need to pass the exact same reference of the object which populate the select into the selected one to get the selected value.

您需要将填充选择的对象的完全相同的引用传递到选定的对象中以获取选定的值。

Here you use a value of all FormControlin referencePrincipalsto populate your selectbox, so to get it selected use this object:

在这里,您使用 all FormControlin的值referencePrincipals来填充您的selectbox,因此要选择它,请使用此对象:

public createPrincipalFormControl(principal) {
    const selectedFormControl = this.referencePrincipals.controls.find(form => form.value.code === principal.code)
    return this.formbuilder.control(selectedFormControl.value);
}

Working plunker. https://plnkr.co/edit/vw3WZ6?p=preview

工作笨蛋。https://plnkr.co/edit/vw3WZ6?p=preview

回答by dee zg

There are at least 2 problems with your approach.

你的方法至少有两个问题。

  1. Your data source here is probably async. Which means you should not do this.initiPrincipals(responseData[0])immediately after var responseDatabut instead in callback of whatever method gets you the data or in a subscription to http service, if you get data through Observable.

    let subscription = myservice.getmedata.subscribe(data => { //here you should do your initializations with data from server };

    If your data comes from @Input() then the right plase is ngOnChanges.

  2. As Fetra pointed out, regardless of the fact that your previously selected option has exactly the same value as the ones you've prepopulated into select list, in order to set it as selected you need exact reference to the ones you've populated it with. so, something like:

    this.formGroup.controls['yourSelectControl'].patchValue(this.yourInitialCollectionOfOptions.find(v => v.propertyByWhichYouWantToCompare == valueFromServer.propertyByWhichYouWantToCompare)

  1. 您这里的数据源可能是异步的。这意味着如果您通过 Observable 获取数据,您不应该this.initiPrincipals(responseData[0])立即执行,var responseData而是在任何方法的回调中获取数据或订阅 http 服务。

    let subscription = myservice.getmedata.subscribe(data => { //here you should do your initializations with data from server };

    如果您的数据来自 @Input() 那么正确的 plase 是ngOnChanges.

  2. 正如 Fetra 指出的那样,无论您之前选择的选项与您预先填充到选择列表中的选项具有完全相同的值,为了将其设置为已选择,您需要准确引用您填充它的选项. 所以,像:

    this.formGroup.controls['yourSelectControl'].patchValue(this.yourInitialCollectionOfOptions.find(v => v.propertyByWhichYouWantToCompare == valueFromServer.propertyByWhichYouWantToCompare)