代码之家  ›  专栏  ›  技术社区  ›  haku

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

  •  0
  • haku  · 技术社区  · 7 年前

    Angular ,有没有办法确定 FormGroup FormControl 在一个 动态 FormArray valueChanges 事件?

    我的 形式阵列 它是动态的。它一开始是空的,用户可以添加 FormGroup 到 形式阵列 点击一个按钮。

    当值更改时,我需要重新验证控件。因为我不知道哪个控件发出了事件,所以我循环了整个过程 并验证所有 / 窗体控件 即使只有一个控件发生了更改,但每次数组中的任何内容都会发生更改。我怎样才能避免这样做?

            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.
        }
    }
    
    4 回复  |  直到 7 年前
        1
  •  18
  •   j3ff    6 年前

    我通过添加一个 formControl (命名为groupIndex)中的 formGroup 跟踪索引并订阅 valueChanges 在 formGroup 水平而不是 formArray 水平。在 事件,然后我可以访问

    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);
    }
    
        2
  •  18
  •   flyer    6 年前

    为了建立在epsilon的答案之上,epsilon的答案收集了一系列的valueChanges观测值并将其合并,您还可以通过管道将值更改传递给管道,通过map将必要的上下文添加到更改流中。

    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>
      }
    }
    

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

    编辑 因为我在看一个FormArray,用户可以通过添加/删除按钮修改它,所以我在takeUntil中添加了一个ChangesSubscribe主题和引用。这使我可以丢弃旧的手表,并在列表更改时设置新的手表。因此,现在我在列表中添加或删除项时调用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);
        });
    }
    
        3
  •  15
  •   epsilon    7 年前

    你可以试试这样的东西,但我不确定它是否管用

    merge(...this.myFormArray.controls.map(control => control.valueChanges))
      .subscribe(this will be one of your form controls' value);
    
        4
  •  2
  •   user3088037    5 年前

     <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);
     }
    
        5
  •  -1
  •   Carlos P.    7 年前

    不是在我的电脑中进行测试,但使用函数的调用者属性可能会引导您朝着所需的方向前进。虽然不建议使用此属性:

    此功能是非标准的,不在标准轨道上。不要在面向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.
         ...
    }
    
    推荐文章