如何识别FormArray中的哪个项目发出了valueChanges事件?

时间:2018-12-06 15:44:41

标签: angular rxjs angular-reactive-forms

Angular中,有一种方法可以识别动态 FormGroup中哪个FormControl / FormArray发出了valueChanges活动吗?

我的FormArray是动态的。它开始是空的,用户可以通过单击按钮将FormGroup添加到FormArray

当valueChanges时,我需要重新验证控件。由于我不知道哪个控件发出了该事件,因此即使只更改了一个控件,我也会遍历整个FormArray / FormGroup / FormControl并进行验证-这是每次数组更改。我该如何避免这样做?

        this.myFormArray
        .valueChanges
        .subscribe(data => this.onValueChanged(data));

    onValueChanged(data?: any): void {

    // the data I receive is an entire form array.
    // how can I tell which particular item emitted the event, 
    // so I don’t need to loop through entire array and run validation for all items.

    for (let control in this.myFormArray.controls) {
        // run validation on each control.
    }
}

6 个答案:

答案 0 :(得分:1)

我通过在formControl中添加formGroup(名为groupIndex)来跟踪索引并在valueChanges级别而不是{ {1}}级。在formGroup事件中,我然后可以访问存储当前索引的formArray

valueChanges

答案 1 :(得分:0)

您可以尝试这样的操作,但我不确定它是否可以工作

merge(...this.myFormArray.controls.map(control => control.valueChanges))
  .subscribe(this will be one of your form controls' value);

答案 2 :(得分:0)

不在我的PC上进行测试,但是也许使用功能的调用者属性可能会引导您朝所需的方向前进。尽管不建议使用此属性:

  

此功能不是标准功能,也不在标准轨道上。请勿在面向Web的生产站点上使用它:它不适用于每个用户。实现之间也可能存在很大的不兼容性,并且将来的行为可能会发生变化。

     

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller

一个粗略的例子:

this.myFormArray
    .valueChanges
    .subscribe(onValueChanged);

onValueChanged(data?: any): void {
     var whoYouGonnaCall = onValueChanged.caller.caller...caller;
     ...
}

更新

另一种方法是存储最后一个表单值,并使用lodash之类的属性进行比较。

var lastFormValue = this.myFormArray.value; // maybe in an init function
this.myFormArray
    .valueChanges
    .subscribe(onValueChanged);

onValueChanged(data?: any): void {
     var diff = _.omitBy(data, function(v, k) {
         return lastFormValue[k] === v;
     });
     this.lastFormValue = this.myFormArray.value; // Update for future requests
     // diff will contain the properties if the form that changed.
     ...
}

答案 3 :(得分:0)

要以epsilon的答案为基础,该答案会收集valueChanges可观察变量的数组并将其合并-您还可以通过管道来传递值更改,通过映射将必要的上下文添加到更改流中。

merge(...this.formArray.controls.map((control: AbstractControl, index: number) =>
        control.valueChanges.pipe(map(value => ({ rowIndex: index, value })))))
      .subscribe(changes => {
        console.log(changes);
      });

输出:

{ 
  rowIndex: 0
  value: {
    <current value of the changed object>
  }
}

请注意,第一个对map的调用(在控件上)在数组上。第二个映射(在管道中)是RxJs映射。我真的很喜欢这个网站,可以帮助这些操作员直截了当,并想象这些事件流是什么样的:https://rxmarbles.com/#map

编辑: 因为我正在观看一个可由用户通过添加/删除按钮修改的FormArray,所以我添加了一个changesUnsubscribe主题,并在takeUntil中引用了该主题。这样,当列表更改时,我就可以丢弃旧的手表并设置新的手表。因此,当我从列表中添加或删除项目时,我现在调用watchForChanges()。

changesUnsubscribe = new Subject();
...
watchForChanges() {
  // cleanup any prior subscriptions before re-establishing new ones
  this.changesUnsubscribe.next();

  merge(...this.formArray.controls.map((control: AbstractControl, index: number) =>
            control.valueChanges.pipe(
                takeUntil(this.changesUnsubscribe),
                map(value => ({ rowIndex: index, control: control, data: value })))))
            .subscribe(changes => {
                this.onValueChanged(changes);
            });
}

答案 4 :(得分:0)

如果有人试图将输入类型=“ number”转换为带有使用角度动态形式的控件数组的整数。

此摘要将返回已更改的实际控件。

const formArray = this.parentFormGroup.controls['obligationList'] as FormArray
    formArray.controls.forEach(control => {
        control.valueChanges
            .debounceTime(800)
            .distinctUntilChanged()
            .takeUntil(this.ngUnsubscribe)
            .subscribe(() => control.patchValue({amountPayable: parseInt(control.value['amountPayable'], 10)}, {emitEvent : false}))
    })

答案 5 :(得分:0)

将FormArray控件绑定到模型中的数组时,可以将模型中的索引或任何其他id传递给事件处理程序:

 <div *ngFor="let m of modelArr; let ix = index" formArrayName="ControlArr">
    <input type="checkbox" [formControlName]="ix" (change)="my_handler($event, ix, m.id)">
 </div>

和组件打字稿中的

 my_handler(ev: any, ix: number, id: any): void {
    console.log(ix, id);
 }