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

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

问题描述

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

谢谢.

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

演示: 演示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>

解决方案

您的问题

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

设计

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

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

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

用抽象的术语来说,您目前拥有

 list1: [];
selected1: [];

list2: [];
selected2: [];
 

如果要将项目从1-> 2中移出,则move方法将删除所有从items1items2的选定项目.简单的.

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

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

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

当您从1-> 2移动项目时,您将继续将项目从list1移动到list2,但是现在您还需要从formArray1中删除相关项目并将其添加到.

我正在存储对表单数组的引用,以便以后使用它们.

构建表单

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

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

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

并按如下所示将其绑定到HTML中:

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

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

移动项目

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

要将项目从列表1移动到列表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;
 

这将循环遍历列表1中的每个选定项目,将其从列表中剪接,推入列表2中.然后将清除选定的项目;

移动表单组

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

 // 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);
});
 

请注意,如何在表单数组之间移动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天全站免登陆