2

If I try to set a FormArray value when the FormArray is empty:

const array1 = new FormArray([]);
array1.setValue(['']);

I get this error:

There are no form controls registered with this array yet. If you're using ngModel, you may want to check next tick

If I add 2 when there is 1:

const array2 = new FormArray([new FormControl('')]);
array2.setValue(['', '']);

I get the following error:

Cannot find form control at index 1

If I try to set 1 when there are 2:

const array3 = new FormArray([new FormControl(''), new FormControl('')]);
array3.setValue(['']);

I get the following error:

Must supply a value for form control at index: 1.

Question: How do I write a function which can update the value of a Form Array with a list of arbitrary length?

StackBlitz: https://stackblitz.com/edit/angular-wduzv9?file=src/app/app.component.ts

*edit I understand that I can use the push(), at(), and removeAt(0) methods. I'm looking for a general-purpose function. It can use any methods you want.

11
  • Why are you doing this? Commented Oct 16, 2019 at 18:18
  • 1
    @Chellappan In one case, I'm syncing the value with another control. In the other, inserting default values conditionally to then be updated at the level of the FormControl. Does it matter, though? Commented Oct 16, 2019 at 18:29
  • FormArray are index based you should acces using at method, like this array2.at(0).setValue(['']); Commented Oct 16, 2019 at 18:32
  • @Chellappan Yes, I understand and appreciate this fact fully. Do you have an answer? Commented Oct 16, 2019 at 18:36
  • try this array2.reset(['',''']) Commented Oct 16, 2019 at 18:38

3 Answers 3

1

Simplify your functions

getFormArray<T>(elements: T[]): FormArray {
    return new FormArray(
      elements.map((x: T) => {
        const obj = new FormGroup({});
        Object.keys(x).forEach(k => {
          obj.addControl(k, new FormControl(x[k]));
        });
        return obj;
      })
    );
  }

//the use, e.g.

this.formArray = this.getFormArray<any>([
  { id: 1, name: "Bob" },
  { id: 2, name: "Peter" }
]);

see stackblitz

Sign up to request clarification or add additional context in comments.

1 Comment

I wanted to edit the FormArray in-place. But this is nice if you don't have that requirement.
1

The problem is that adding new values at new keys or indexes can't be done without knowing what type of AbstractControl you want inside the FormArray at the new index.

Any solution must take into account whether that index should be a FormControl, FormGroup, or FormArray, hence the toSubControl parameter I offer in my setFormArrayValue function. (I also provide some example values to supply to this parameter.)

I opted to remove all items and then add new controls per value. Trying to preserve the controls already there was producing some weird behavior.

This is the solution I came up with:

type ControlFactory<T> = (values: T) => AbstractControl;

export function setFormArrayValue<T>(
    array: FormArray,
    values: T[],
    toSubControl: ControlFactory<T> = toFormControl
) {
    while (array.length) {
        array.removeAt(0);
    }
    values
        .map(toSubControl)
        .forEach(function(control) {
            array.push(control);
        });
    return array;
}

Where the following functions can create the new child AbstractControls.

// When the child controls should be FormControl
function toFormControl<T>(value: T) {
    return new FormControl(value)
}

// When the child controls should be FormGroup
function toFormGroup<T>(item: T) {
    const fields = Object.entries(item).reduce(function(hash, [key, value]) {
        hash[key] = new FormControl(value);
        return hash;
    }, {});
    return new FormGroup(fields);
}

// When the child controls should be FormArray of FormControls
function toFormArray<T>(values: T) {
    const fields = values.map(value => new FormControl(value));
    return new FormGroup(fields);
}

1 Comment

I improve (I hope) your function in a new answer
0

You can initialize an empty formArray and then add the controls and then set the value,


const arr = new FormArray([]);

const a = new FormControl('a', []);
const b = new FormControl('b', []);
const c = new FormControl('c', []);

arr.push(a);
arr.push(b);
arr.push(c);

console.log(arr.value);   // ["a", "b", "c"] 
// arr.setValue([0]);

console.log(arr) 
arr.controls[0].setValue('xxx')

console.log(arr.value);   // "xxx", "b", "c"

1 Comment

How do I make a function out of this to solve problems in my app?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.