1
votes

I've got a formarray. If I initialize it with a formbuilder and push onto it, it seems to work okay. If I take the same array that I'm pushing items from, and try to add it all at once with the spread operator, I get the error "ERROR Error: Cannot find control with path: 'sizesArray -> 0'"

My question is, in keeping with the spirit of ngrx/rsjx/functional-reactive programming, how can I keep the Formarray 'immutable' and assign to it from the FormGroup[] rather than clearing, looping and pushing?

Here's the relevant code:

sizesArray: FormArray;
sizesArray2: FormArray;
sizes: Observable<SizeModel[]>;

constructor(
    private productStore: Store<productState.State>,
    private fb: FormBuilder
  ) {
    this.sizes = productStore.select(productState.selectSizes);
  }

ngOnInit() {


    this.sizesArray = this.fb.array([]);
    this.sizesArray2 = this.fb.array([]);

    this.sizes
      .pipe(
        skipWhile(sizes => !sizes),
        map(sizes => {
         return sizes.map(size => this.fb.group({size}));
        }
       )
      )
      .subscribe(
        sizes => {
          // this is the code I want to use.  that throws the error
          this.sizesArray = this.fb.array([...sizes]);

          // this code works, but I don't know how it's different than the code above
          this.sizesArray2.clear();
          sizes.forEach( size => {
            this.sizesArray2.push(size);
          });

        }
      );

    this.sizeForm = this.fb.group({
      sizesArray: this.sizesArray
    });

  }

and my template:

...
<form [formGroup]="sizeForm">
      <fieldset  id="size-container">
        <legend>Sizes</legend>
        <div formArrayName="sizesArray" >
            <div *ngFor="let size of sizesArray.controls; let i = index;" [formGroupName]="i">
            <app-size-form
              formControlName="size"
              (deleteClicked)="deleteSize(i);">
            </app-size-form>
          </div>
        </div>
      </fieldset>
      <button (click)="addSize()" id="size-button">Add Size</button>
      <button mat-button matStepperPrevious>Back</button>
      <button mat-button (click)="submit()">Save</button>
    </form>
...

Thanks!

1
you can not use spread operator with an object so complex as a formGroup, (spread operator give a copy of "properties" but not methods, yes you has,e.g. a "property" setValue, but not a "method" setValue).NOTE: I think that if you use this.fb.array(sizes) it must be work - Eliseo
@eliseo - good point. I'm planning to bring in lodash's cloneDeep to remedy it. Thanks! - jmiles540

1 Answers

1
votes

I think the problem is that you're changing the references.

this.sizeForm = this.fb.group({
  sizesArray: this.sizesArray
});

At this point sizeArray points to the value referenced by this.sizesArray, which is this.fb.array([]). Then, in your subscriber's next callback, you're changing the references. This value is also 'captured' by formControlName='sizesArray', meaning that

<div *ngFor="let size of sizesArray.controls; let i = index;" [formGroupName]="i">

Will create a number of FormGroup instances, which are the children of a container sizesArray, which is empty.

Now, this.sizesArray(on which you're iterating over in the template) points to something else, namely: this.fb.array([...sizes]).

So, what's basically happening is that sizesArray(the child of sizeForm) points to an empty FormArray and this.sizesArray points to a non-empty FormArray.

What you could to in this case would be this:

.subscribe(sizes => {
  this.sizeForm.setControl('sizeArray', this.fb.array(sizes))
})