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

NoS*_*ler 5 rxjs angular angular-reactive-forms

Angular,有一个方法来识别FormGroup/ FormControl在一个动态FormArray发射的valueChanges事件?

FormArray是动态的。它开始是空的和用户可以添加FormGroupFormArray通过点击一个按钮。

当valueChanges时,我需要重新验证控件。由于我不知道哪个控件发出了该事件,因此我将遍历整个控件FormArray并验证所有FormGroupFormControl即使仅更改了一个控件),这是每次数组中的任何内容发生更改时。我如何避免这样做?

        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.
    }
}
Run Code Online (Sandbox Code Playgroud)

fly*_*yer 16

要建立在 epsilon 的答案之上,它收集了一组 valueChanges 可观察对象并将它们合并 - 您还可以通过管道传输值更改,这会通过映射向更改流添加必要的上下文。

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

输出:

{ 
  rowIndex: 0
  value: {
    <current value of the changed object>
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,第一次调用 map(在控件上)是在一个数组上。第二张地图(在管道中)是一个 RxJs 地图。我真的很喜欢这个网站来帮助让这些运营商直接了解这些事件流的样子:https : //rxmarbles.com/#map(编辑:我现在认为这篇文章要好得多:https : //indepth.dev /learn-to-combine-rxjs-sequences-with-super-intuitive-interactive-diagrams/ )

编辑:因为我正在观看可以由用户通过添加/删除按钮修改的 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);
    });
}
Run Code Online (Sandbox Code Playgroud)


eps*_*lon 15

你可以尝试这样的事情,但我不确定它会起作用

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

  • 惊人的解决方案。 (2认同)

NoS*_*ler 5

我通过在中添加formControl(名为groupIndex)formGroup来跟踪索引并valueChanges在该formGroup级别(而非formArray级别)订阅来解决了此问题。上valueChanges的事件,然后我可以访问formControl存储当前索引。

this.myMainFormGroup = this.myFormBuilder.group({
    // other formControls
    myFormArray: this.myFormBuilder.array([this.addArrayItem()])
});

// this method will be called every time the add button is clicked
addArrayItem(): FormGroup {
  const itemToAdd = this.myFormBuilder.group({
    // dont forget add input control for groupIndex in html for this. It will give error otherwise.
    // i made the input control hidden and readonly
    groupIndex:"", 
    firstName:["", [validator1, validator2]]
    //other formControls

  });

  const myFormArray = <FormArray>this.myMainForm.get("myFormArray");

  //set groupIndex
  itemToAdd.get("groupIndex").patchValue(myFormArray.length -1);

  //subscribe to valueChanges
  itemToAdd.valueChanges
    .debounceTime(200)
    .subscribe(data => this.onValueChanged(data));

  myFormArray.push(itemToAdd);
}

onValueChanged(data?: any): void {
      const groupIndex = data["groupIndex"];

      const myChangedGroup = <FormArray>this.myMainForm.get("myFormArray").controls[groupIndex];

      // now I have hold of the group that changed without having to iterate through the entire array. 
      // run through the custom validator 
      this.generalValidator(myChangedGroup);
    }
Run Code Online (Sandbox Code Playgroud)

  • 太棒了!这正是我想要的。 (2认同)