剑道:将顶部的 FormGroup 插入 FormArray

Posted

技术标签:

【中文标题】剑道:将顶部的 FormGroup 插入 FormArray【英文标题】:Kendo : Insert a FormGroup at a top into a FormArray 【发布时间】:2020-01-26 01:37:37 【问题描述】:

问题陈述:

在下面的 stackblitz 演示中,我可以在单击 Add 按钮并从一个控件触发 (blur) 事件以更新另一个共享相同控件的值时,在网格的开头动态插入 FormGroup rowIndex。现在的问题是,如果我插入多个formGroups 并在任何新添加的FormGroups 上触发(blur) 事件,它会更新其他控件的值以及具有不同的rowIndex

重现问题的步骤:

打开下面的演示链接。 通过单击“添加”按钮添加多行(2 行)。 在新添加的第二行的名称字段中输入值并聚焦。 它更新两个新添加的行中的年龄字段,而不是仅在第二行中。

Stackblitz 演示: https://stackblitz.com/edit/angular-oitz5j-4uezqr

要求:

标记特定行的任何名称字段,同一行的年龄字段应显示随机数,而不会影响其他行。

到目前为止我尝试了什么?

尝试使用 Array unshift() 方法但出现错误

类型“FormArray”上不存在属性“unshift”

尝试使用FormArray.insert() 方法。如果我们想在特定索引处添加FormGroup,它将起作用。但它无法在FormArray顶部动态插入多个FormGroups

更新:上述问题在没有 Kendo Grid 的情况下运行良好。请查看以下演示。

演示: https://stackblitz.com/edit/angular-oitz5j-2a31gs

github/*** 中的相关问题:

https://github.com/angular/angular/issues/16322

Angular - Form Array push specific index

注意:如果我们要在 Grid 的末尾添加控件,它会像冠军一样工作。请检查下面的演示,它在最后添加控件时效果很好。

演示: https://stackblitz.com/edit/angular-oitz5j-aszrjq

【问题讨论】:

对于第一个演示,当我尝试复制这些步骤时,我可以在控制台中看到错误。 stackblitz 有什么变化吗? @PankajParkar 很抱歉这个错误。我纠正了这一点。可以请您现在检查吗 这是一个有趣的部分:尝试调用函数addNewRow() 两次以添加行和onFocusOut(1) 以模拟预期的行为,一切正常! 试图解决问题:每次数据更改时,您都在创建新的表单组,每次按下添加按钮时,您都会在 addNewRow() 函数和 subscription 中插入新组. @SachinGupta 我正在更新数据源和控件。您可以在没有剑道的情况下查看演示,并且运行良好。 【参考方案1】:

对于那些想要在 FormArray 中动态插入元素的人,请使用索引为 0 的 Insert。

this.formName.controls.FieldNameArray.insert(0,[...])

https://angular.io/api/forms/FormArray#insert.

【讨论】:

【参考方案2】:

为什么我的代码不起作用?

这根本不是剑道问题。每次触发 view observable 时,您的整个表单都已创建完毕。因此,当您添加一行时,您基本上将添加到createForm 中的items 以及触发view 可观察对象(通过调用updateGridData),这反过来又添加到@987654329 中的items @。 Telerik 似乎在做后者,如example 所示,所以我们根本不需要在addNewRow 中将insert 转换为items。相反,只需更新gridData,然后调用updateGridData

解决方案

您的代码中只有两个问题。第一个在您的view 订阅块中。您使用的代码基本上只是在之前的 items 之上推送而不是替换它们。所以我们需要在这里重新初始化itemsFormArray

this.view.subscribe(r => 
    this.createForm.setControl('items', new FormArray([]));

    r.data.forEach((i) => 
        const newRow = new FormGroup(
            name: new FormControl(i.name),
            age: new FormControl(i.age)
        );
        (this.createForm.get("items") as FormArray).push(newRow);
    );
);

您可以使用map 重写此代码,如下所示。

this.view.subscribe(r => 
    this.createForm.setControl('items', new FormArray(r.data.map(item =>
        new FormGroup(
            name: new FormControl(item.name),
            age: new FormControl(item.age),
        )
    )));
);

现在您需要做的就是在添加新行时分配gridData。我们需要从createForm 获取用户输入的数据,因为gridData 没有绑定到任何地方,也不会包含用户输入的数据。由于view subscribe 块已经负责声明createForm,因此无需对此处的表单执行任何操作。

addNewRow() 
    const blankRow = 
        name: "",
        age: ""
    ;

    this.gridData = [blankRow, ...this.createForm.get('items').value];
    const data: GridDataResult =  data: this.gridData, total: this.gridData.length ;
    this.editService.updateGridData(data);

网格现在应该可以工作了。

Forked StackBlitz

注意:将新行推到网格末尾的示例有效的原因是您只在ngOnInit 的开头声明了一次createForm。然后手动将gridDatainsert 更新为items。这里没有updateGridData 触发view 重新创建createForm。如果有,即使这样也不会按预期工作。

【讨论】:

您根据我们的要求建议的解决方案存在一个问题。每次我们添加一个新的表单组时都重新启动 FormArray - 假设我们已经有 n 个 FormGroup 并在控件上进行了一些自定义验证。因此,如果我们再次初始化整个 FormArray 只是为了添加一个新的 FormGroup 将重置所有现有的验证,并可能导致性能问题再次使用 n 个 FormGroup 重新创建整个 FormArray。请提出建议。【参考方案3】:

我是如何开始调试的:

我并不是说这并不奇怪,但是当您调用该方法 FormArray.insert 时,它会插入现有 formArray 的副本并与当前状态连接。我添加了 console.log(formArray.controls) 并检查它是否呈指数增长,这意味着每次插入都会添加一个快照。

这让我发现您订阅了 view Observable,并且每当它发生变化时,您都会使用现有列表推送网格中的所有项目。


如何解决:

第 1 步:在您的 addRow 方法中删除 formGroup 的创建并插入到数组中,因为它已在您的 view.subscribe 方法中处理

addNewRow() 
  ...
  /*
  <--- remove the creation of formgroup and adding to formarray taken care in next step --->
  if (this.createForm) 
     (this.createForm.get("items") as FormArray).insert(2, newRow);
  
  */

第 2 步:在您的 view.subscribe 方法中从 push 更改为 insert。所以初始项目也将被插入而不是推送

this.view.subscribe(r => 
  r.data.forEach((data, i) =>  //<-- Added index of the array
      const newRow = new FormGroup(
        name: new FormControl(data.name),
        age: new FormControl(data.age),
      );
      if (this.createForm) 
        (this.createForm.get("items") as FormArray).insert(i, newRow);//<--changed to insert
      
  );
);

第 3 步现在您要取消换档

addNewRow() 
  const blankRow = 
    name: "",
    age: "",
  ;
  this.gridData.unshift(blankRow); //<-- you do it here.
  const data: GridDataResult =  data: this.gridData, total: this.gridData.length, 
  this.editService.updateGridData(data);


更新 Stackblitz 供您参考:https://stackblitz.com/edit/angular-oitz5j-ulhs4k

【讨论】:

这里不能使用unshift。请注意,如果您在 StackBlitz 中添加一行,在其中输入数据,然后再添加另一行,您刚刚输入的数据将消失。 您根据我们的要求建议的解决方案存在一个问题。每次我们添加一个新的表单组时都重新启动 FormArray - 假设我们已经有 n 个 FormGroup 并在控件上进行了一些自定义验证。因此,如果我们再次初始化整个 FormArray 只是为了添加一个新的 FormGroup 将重置所有现有的验证,并可能导致性能问题再次使用 n 个 FormGroup 重新创建整个 FormArray。请提出建议。【参考方案4】:

问题是剑道网格。您一遍又一遍地踩在其数据源的索引 0 上 谁知道他会在内部做什么来显示数据。但事实是它没有使用表单数组值。如果您从 kendo 网格数组中取出将正常工作,或者如果您在 onFocusOut 方法的末尾添加控制台日志console.log(this.createForm.get("items").value);,您将看到 formArray 的值很好。

如果您需要使用是或是剑道网格。阅读以下文档:

https://www.telerik.com/kendo-angular-ui/components/grid/editing/editing-reactive-forms/

我看到的示例表明您无法在外部处理表单。您需要使用剑道网格提供的方法。因为剑道网格并没有真正显示 fromArray 值

【讨论】:

是的,问题出在 Kendo Grid 上,但是如果我们在 FormArray 的末尾推送 FormGroup,它的工作正常。唯一的问题是在顶部添加。 是的。因为当您在不存在的索引中添加元素时,剑道网格没有任何与该索引映射的项目。当你步进索引 0 时会破坏剑道网格的内部映射。请参阅示例。 Kendo 使用内部对象向数据源添加新项目。

以上是关于剑道:将顶部的 FormGroup 插入 FormArray的主要内容,如果未能解决你的问题,请参考以下文章

剑道条形图 - 顶部的系列标签?

无法绑定到“formGroup”,因为它不是“form”的已知属性

无法绑定到“formGroup”,因为它不是登录组件中“form”的已知属性

无法绑定到“formGroup”,因为它不是 Angular 中“form”的已知属性

剑道 DropDownListFor() 与 ASP.NET-MVC

Angular2 - 无法绑定到“formGroup”,因为它不是“form”的已知属性