Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.7k views
in Technique[技术] by (71.8m points)

angular - How to bind array of objects to formGroup and formarray using angular8

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>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...