Angular 角度反应形式数组,更改检测覆盖形式

Angular 角度反应形式数组,更改检测覆盖形式,angular,formarray,Angular,Formarray,我使用以下内容作为我为FormArray设计表单的基础。我试图做的jist就是保持表单在页面其他地方的更改是最新的,但在此表单中切换布尔/复选框。(用户有他们选择的卡片列表,此表单显示此选择的列表) 不幸的是,ngOnChanges似乎在不断更新表单,我的更改被覆盖。在我的构造函数中,我检测值的变化,并希望发出这些变化。但是, this.contextSummaryForm.dirty 始终为假。rebuildForm()上的断点表明该方法每秒被调用多次,因此将contextItem.Ised

我使用以下内容作为我为FormArray设计表单的基础。我试图做的jist就是保持表单在页面其他地方的更改是最新的,但在此表单中切换布尔/复选框。(用户有他们选择的卡片列表,此表单显示此选择的列表)

不幸的是,
ngOnChanges
似乎在不断更新表单,我的更改被覆盖。在我的构造函数中,我检测值的变化,并希望发出这些变化。但是,

this.contextSummaryForm.dirty
始终为假。rebuildForm()上的断点表明该方法每秒被调用多次,因此将contextItem.IsedetEnable更改为false将被完全覆盖。我可以阅读我的逻辑,看看为什么会发生这种情况——但我真的不明白我应该怎么做才能允许从其父组件更新contextList并允许用户在这里更新表单

构造函数和更改检测

一旦改变


总而言之:我需要一种方法来使用从输入绑定构建的FormArray,该绑定可以跟上变化,而不会快速覆盖表单。

根据angular的文档

OnChanges:当指令的任何数据绑定属性发生更改时调用的生命周期挂钩

话虽如此,覆盖表单的不是变更检测,也不是onChanges钩子。作为最佳实践,我们应该只构建一次表单,并使用FormArray的方法来干扰数组

您可以使用FormArray方法和直接在数组上推送项,而不是重建表单。我假设您面临的问题是,无论数据是什么,您都在重建表单

我的意思是:认为你有两个组成部分。孩子和父母。子级负责操作数据(添加、删除),父级负责在表单上显示这些数据。尽管这样做看起来很简单,但您必须过滤掉子组件已经处理过的任何项

在您的实现中,尽量不要
在onChanges上重建
表单,而是在数组上推送项

您应该将哪些项目推送到阵列?(过滤掉所有已处理的项目)

这是一种可以解决您的问题的方法

ngOnChanges(changes: SimpleChanges) {
    for (let propName in changes) {
      if (propName === 'contextList') {

        const contextItemsToInsert = 
          this.contextList.filter((it: any) => !this.plans.value.map(pl => pl.id).includes(it.id));

            contextItemsToInsert.forEach((x: any) => {

              this.plans.push(this.fb.group({
                id: x.id,
                name: x.name,
                isEditEnabled: x.isEditEnabled,
              }))
            })

            // similar approach for deleted items
      }
    }
  }

  ngOnInit() {
    this.createForm();
  }

  createForm(): void {
    this.contextSummaryForm = this.fb.group({
      plans: this.fb.array([this.initArrayRows()])
    });
  }

  initArrayRows(): FormGroup {
      return this.fb.group({
        id: [''],
        name: [''],
        isEditEnabled: ['']
    });
  }
在这里,您可以找到一个工作示例

这不是一个完全有效的例子,但它有助于抓住问题的关键


我希望我正确地理解了您面临的问题是

能否尝试比较ngOnChanges中contextList的新旧值,并且仅在它们不同时调用rebuild?很好的建议,让我今天就尝试一下,我会回来报告。我认为它缺少父组件代码。ngOnChanges被多次调用的事实可能是由于父级提供ContextList的方式。您是否可以发布当前组件的父级代码(ts | html)和html。今天上午我将尝试一下。我出去了。
createForm(): void {
 this.contextSummaryForm = this.fb.group({
  plans: this.fb.array([this.initArrayRows()])
 });
}

initArrayRows(): FormGroup {
  return this.fb.group({
   id: [''],
   name: [''],
   isEditEnabled: [''],
});
}
  ngOnChanges(changes: SimpleChanges) {
for (let propName in changes) {
  if (propName === 'contextList') {
    if (this.contextList) {
      this.rebuildForm();
      this.isLoaded = true;
    }
  }
}
}

rebuildForm() {
  this.contextSummaryForm.reset({
  });
  //this.fillInPlans();
  this.setPlans(this.contextList);
}


  setPlans(items: ContextItem[]) {
    let control = this.fb.array([]);
    items.forEach(x => {
      control.push(this.fb.group({
        id: x.plan.id,
        name: x.plan.Name,
        isEditEnabled: x.isEditEnabled,
      }));
    });
    this.contextSummaryForm.setControl('plans', control);
  }
const contextItemsToInsert = 
          this.contextList.filter((it: any) => !this.plans.value.map(pl => pl.id).includes(it.id));
ngOnChanges(changes: SimpleChanges) {
    for (let propName in changes) {
      if (propName === 'contextList') {

        const contextItemsToInsert = 
          this.contextList.filter((it: any) => !this.plans.value.map(pl => pl.id).includes(it.id));

            contextItemsToInsert.forEach((x: any) => {

              this.plans.push(this.fb.group({
                id: x.id,
                name: x.name,
                isEditEnabled: x.isEditEnabled,
              }))
            })

            // similar approach for deleted items
      }
    }
  }

  ngOnInit() {
    this.createForm();
  }

  createForm(): void {
    this.contextSummaryForm = this.fb.group({
      plans: this.fb.array([this.initArrayRows()])
    });
  }

  initArrayRows(): FormGroup {
      return this.fb.group({
        id: [''],
        name: [''],
        isEditEnabled: ['']
    });
  }