如何使用angular8将对象数组绑定到formGroup和formarray [英] How to bind array of objects to formGroup and formarray using angular8

查看:20
本文介绍了如何使用angular8将对象数组绑定到formGroup和formarray的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用反应形式将对象数组绑定到 formArray,但是在这里如果我选择一个部分中的任何一项并单击向右移动/向左移动,那么整个对象已从一个部分移动到其他.但该功能运行良好,但我无法检测到 form.valuechanges() 中从一个部分到另一部分的更改.任何人都可以帮助我检测更改,以防项目已从一个部分移动到另一部分,在这里我想要更改检测,但功能必须按原样工作,我的意思是将整个对象从一个部分移动到另一部分.

提前致谢.

我在这里有工作 demo2,我希望我之前发布的 demo1 像这个 demo2 一样工作,formarray 工作.演示 2

演示:演示 1

TS:

 私有设置GroupInfoForm() {如果(!this.agentDetailsList){//添加this.agentGroupViewInfoForm = this.FB.group({agentGroupView: this.FB.array(this.agentInView.map(x=>(x))),})} 别的 {//编辑如果(this.agentDetailsList){this.agentGroupViewInfoForm = this.FB.group({agentGroupView: this.FB.array(this.agentInView.map(x=>(x))),})}this.agentGroupViewInfoForm.valueChanges.subscribe(data => {this.formEdit = true;console.log('agentGroupViewInfoForm', this.formEdit)})}}

HTML:

 

<ul class="list-group list-group-flush" *ngFor="let child of agentInView" name="agentInGroup" formArrayName="agentGroupView"><li class="list-group-item {{isInActiveItems(child) ? 'active' : ''}}" (click)="toggleActiveItem(child)">{{child.value}}

解决方案

你的问题

  • 您有 2 个相同类型的项目数组
  • 您想为每个数组呈现一个表单数组
  • 您希望能够在数组之间移动项目
  • 您希望能够将表单值绑定回提交时的项目

设计

与 Angular 中几乎所有基于组件的问题一样,您应该首先考虑模型.您的模型是王道,其他一切都是围绕它构建的.

在您的演示中,您是在列表之间移动项目.您正在更新模型并将 HTML 绑定到该模型.没有错 - 这是正确的方法并且有效.

这里增加的挑战是您还想移动表单组.我们仍然需要先考虑模型,但也要考虑在我们在列表之间移动项目的同时移动相关状态.

抽象地说,您目前拥有

list1: [];选择 1: [];列表2:[];选择 2: [];

如果您想将项目从 1 -> 2 移动,您的移动方法会将所有选定的项目从 items1 删除到 items2.简单的.

添加表单后,您将拥有如下结构:

list1: [];选择 1: [];表单1:表单组;formArray1:FormArray;列表2:[];选择 2: [];表单2:表单组;formArray2:FormArray;

当你从 1 -> 2 移动项目时,你会继续将项目从 list1 移动到 list2,但现在你还需要删除相关的项目来自 formArray1 并将其添加到 formArray2.

我正在存储对表单数组的引用,以便以后更容易使用.

构建表单

使用表单数组可以说是这个答案中最复杂的部分.表单数组的关键是 HTML 结构模仿您构建的 FormGroup 对象的结构.

我们可以使用 FormBuilder 从一个数组构建一个表单并像这样绑定到它:

component.ts

buildForm() {this.model = [{值:'a'},{值:'b'},{值:'c'}];//为模型中的每个项目创建一个表单组const formGroups = this.model.map(x => this.formBuilder.group({值:this.formBuilder.control(x.value)}));//为组创建一个表单数组const formArray = this.formBuilder.array(formGroups);//创建顶级表单this.form = this.formBuilder.group({数组:formArray});}

并像这样在 HTML 中绑定到它:

<div formArrayName="数组"><div *ngFor="let item of list1; let i = index" [formGroupName]="i"><input formControlName="value"/>

<按钮>提交</表单>

这将为数组中的每一项生成一个输入,分别包含值a"、b"、c".

移动物品

在数组之间移动项目是一个简单的 JavaScript 问题.我们需要拼接源数组并push到目标数组.

要将项目从列表 1 移动到列表 2:

//从模型 1 中移动所选项目this.selected1.forEach(item => {const index = this.list1.indexOf(item);this.list1.splice(index, 1);this.list2.push(item);});this.selected1.length = 0;

这将遍历列表 1 中的每个选定项目,将其从列表中拼接,将其推送到列表 2.然后将清除选定项目;

移动表单组

您将在移动项目的同时移动表单组.它在概念上是相似的——你从一个中删除并添加到另一个中.您根据模型构建了表单数组,因此您知道索引匹配.

//从模型 1 中移动所选项目this.selected1.forEach(item => {const index = this.list1.indexOf(item);const formGroup: FormGroup = this.formArray1.controls[index] as FormGroup;this.list1.splice(index, 1);//在表单数组之间移动this.formArray1.removeAt(index);this.formArray2.push(formGroup);this.list2.push(item);});

请注意这里有 2 行要在表单数组之间移动,看起来类似于用于在常规数组之间移动的 2 行.

formArray.removeAt(index)formArray.push(formGroup) 正在移动.与表单数组的不同之处在于,我们需要首先使用 this.formArray1.controls[index] as FormGroup;.

来获取对它的引用

演示:https://stackblitz.com/edit/angular-3cwnsv

注意

在这种设计中,从数组和表单中删除和添加的顺序很重要.您将 HTML 绑定到数组和表单.您正在通过循环遍历数组来创建输入数组,并将每个项目绑定到表单数组中的第 i 组.如果您先从表单中删除,您现在将在数组中有 n 项,在表单数组中有 n - 1 项.尝试绑定到未定义的 n 个表单组时会出错.

您现在正在执行具有多项操作的事务.

除了确保以正确的顺序删除和添加之外,解决此问题的一种方法是使用 OnPush 更改检测.仔细阅读此内容,因为它适用于除最简单的应用之外的所有应用.

后续步骤

为了回答的目的,我让我的演示保持简单.它并不是特别具有可扩展性或可重用性.有很多重复的代码和错误的命名来尝试避免被与您的应用程序相关的嵌套组件和属性名称分散注意力.

实际上,您可能希望创建一个子组件来负责我复制的许多代码.这个设计肯定超出了这个问题的范围.

Hi i have used reactive forms to bind array of objects to formArray, but here if i select any one item in one section and click of move to right/move to left, then the entire object has been moved from one section to other. But that functionality is working fine but i am unable to detect the chnages made from one section to other in the form.valuechanges(). Can anyone help me to detect the changes incase items has been moved from one section to other, and here i want the change detection but the functionality must work as it is, i mean the entire object to be moved from one section to other.

Thanks in advance.

I have working demo2 here, i want my previously posted demo1 to work like this demo2 with formarray working. DEMO 2

DEMO: DEMO 1

TS:

 private settingsGroupInfoForm() {
    if (!this.agentDetailsList) {
      // Add
     this.agentGroupViewInfoForm = this.FB.group({
          agentGroupView: this.FB.array(
            this.agentInView.map(x=>(x))
          ),
        })
    } else {
      // Edit
      if (this.agentDetailsList) {

       this.agentGroupViewInfoForm = this.FB.group({
          agentGroupView: this.FB.array(this.agentInView.map(x=>(x))),

      })
      }
      this.agentGroupViewInfoForm.valueChanges.subscribe(data => {
          this.formEdit = true;
          console.log('agentGroupViewInfoForm', this.formEdit)
        })
      }
    }

HTML:

 <div class="card-body overflow-auto py-0" *ngIf="agentGroupViewInfoForm"
            [formGroup]="agentGroupViewInfoForm">
                <ul class="list-group list-group-flush" *ngFor="let child of agentInView"   name="agentInGroup" formArrayName="agentGroupView">
                  <li class="list-group-item {{isInActiveItems(child) ? 'active' : ''}}" (click)="toggleActiveItem(child)">
                    {{child.value}}
                  </li>
                </ul>
              </div>

解决方案

Your problem

  • You have 2 arrays of items of the same type
  • You want to render a form array for each array
  • You want to be able to move items between the arrays
  • You want to be able to bind the form values back to the items on submit

The design

As with pretty much every component-based problem in Angular, you should think about model first. Your model is king, and everything else is built around it.

In your demo, you are moving items between lists. You are updating your model and binding your HTML to that. Nothing wrong there - it's the right approach and works.

The added challenge here is that you also want to move form groups around as well. We still need to think model-first, but also think about moving related state at the same time we move items between lists.

In abstract terms, you currently have

list1: [];
selected1: [];

list2: [];
selected2: [];

If you want to move items from 1 -> 2, your move method will remove any selected items from items1 to items2. Simple.

Once you add forms, you will have a structure like this:

list1: [];
selected1: [];
form1: FormGroup;
formArray1: FormArray;

list2: [];
selected2: [];
form2: FormGroup;
formArray2: FormArray;

And when you move items from 1 -> 2, you will continue to move items from list1 to list2, but now you will also need to remove the related item from formArray1 and add it to formArray2.

I am storing a reference to the form arrays to make them easier to work with later.

Building the form

Working with form arrays is arguably the most complex part of this answer. The key with form arrays is that the HTML structure mimics the structure of the FormGroup object you build.

We can use FormBuilder to build a form from an array and bind to it like this:

component.ts

buildForm() {
  this.model = [ 
    { value: 'a' }, { value: 'b' }, { value: 'c' }
  ];

  // create a form group for each item in the model
  const formGroups = this.model.map(x => this.formBuilder.group({
    value: this.formBuilder.control(x.value)
  }));

  // create a form array for the groups
  const formArray = this.formBuilder.array(formGroups);

  // create the top-level form
  this.form = this.formBuilder.group({
    array: formArray
  });
}

And bind to it in the HTML like this:

<form [formGroup]="form1" (submit)="onSubmit()">
  <div formArrayName="array">
    <div *ngFor="let item of list1; let i = index" [formGroupName]="i">
      <input formControlName="value" />
    </div>
  </div>
  <button>Submit</button>
</form>

This will generate an input for each item in the array, containing the values "a", "b", "c" respectively.

Moving items

Moving items between arrays is a simple javascript problem. We need to splice the source array and push to the destination array.

To move items from list 1 to list 2:

// move selected items from model 1
this.selected1.forEach(item => {
  const index = this.list1.indexOf(item);
  this.list1.splice(index, 1);
  this.list2.push(item);
});

this.selected1.length = 0;

This will loop through each selected item in list 1, splice it from the list, push it to list 2. It will then clear the selected items;

Moving form groups

You will move form groups at the same time as you move items. It is similar in concept - you remove from one and add to the other. You built your form array from your model, so you know your indexes match.

// move selected items from model 1
this.selected1.forEach(item => {
  const index = this.list1.indexOf(item);
  const formGroup: FormGroup = this.formArray1.controls[index] as FormGroup;

  this.list1.splice(index, 1);

  // move between form arrays
  this.formArray1.removeAt(index);
  this.formArray2.push(formGroup);

  this.list2.push(item);
});

Notice here how there are 2 lines to move between form arrays that look similar to the 2 lines used to move between regular arrays.

formArray.removeAt(index) and formArray.push(formGroup) are doing the moving. The difference with the form array is that we need to get a reference to it first using this.formArray1.controls[index] as FormGroup;.

DEMO: https://stackblitz.com/edit/angular-3cwnsv

Caution

In this design the order in which you remove from and add to the arrays and forms is important. You are binding your HTML to both your arrays and your form. You are creating the array of inputs by looping over your array, and binding each item to the ith group in the form array. If you remove from the form first, you will now have n items in the array and n - 1 items in your form array. You will have an error when trying to bind to an undefined nth form group.

You are now performing a transaction with multiple operations.

Aside from ensuring you remove and add in the correct order, one way around this is to use OnPush change detection. Read up on this, as it's a good strategy for all but the simplest apps.

Next steps

I kept my demo simple for the purposes of the answer. It isn't particularly scalable or reusable as it stands. There is a lot of repeated code and bad naming to try and avoid getting distracted by nested components and property names that relate to your application.

In reality you would probably want to create a child component that is responsible for a lot of the code that I have duplicated. The design of that is definitely out of scope for this question.

这篇关于如何使用angular8将对象数组绑定到formGroup和formarray的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆