How to update FormArray value with variable length

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.

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

angular 2 remove all items from a formarray, is one of the three fundamental building blocks used to define forms in Angular, along with FormControl and FormGroup . When we pass array with length two, it sets value to two controls and when we pass array with length one, it sets value to one control. FormArray setValue() and patchValue() Example using FormBuilder We will create a reactive form using FormBuilder .

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"

Angular FormArray setValue() and patchValue(), Angular FormArray tracks the value and validity state of an array of We can observe that the array we pass to setValue() has length three that is patchValue() will try to patch value at its best and it will patch the given array  content_copy const arr = new FormArray ([new FormControl (), new FormControl ()]); console. log (arr. length); // 2 arr. clear (); console. log (arr. length); // 0 It's a simpler and more efficient alternative to removing all elements one by one:

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

FormArray, Tracks the value and validity state of an array of FormControl , FormGroup or emit events with the latest status and value when the control value is updated. new FormArray([ new FormControl(), new FormControl() ]); console.log(arr.length​); // 2 to 'blur', unless the child has explicitly specified a different updateOn value. To achieve this, we will be using FormArray, which accepts an array of Form Groups or Form Controls. We will be grouping the form controls for a single contact, under a single form group. Then this multiple form groups will be added into the FormArray. Each form group will contain three form controls: Contact Type, Label and Value.

Building Modern Web Applications Using Angular, If we need to update FormGroup partially from a superset or subset, we can use the patchValue() method: We can use FormArray to create a form of variable or unknown length: patchValue(['London','W5']); //access form array value  Reactive-forms in Angular is very powerful to handle changes in value, check the statuses, and perform validations on a form-field in particular form because of supporting FormGroup, FormControl

FormArrayName, Syncs a nested FormArray to a DOM element. See more. The name corresponds to a key in the parent FormGroup or FormArray . Accepts a name as a string  In this program, we use the for loop to add the total value of the variables. Finally, we divide the total value by the length of the array. The result is the average. Of course, instead of using this program you could always directly use the Average function. Check out this Excel VBA course to see how it’s done.

FormArray length does not reset by form.reset() - angular - html, FormArray length does not reset by form.reset() - angular. That means you will update the DOM dynamically. Hence, calling reset on the FormArray will only clear the values of the dynamically formGroup variable addForm: let formTemp: any = { selectedCompany: new FormControl(null, [Validators.required]), } this. So you have a form and would like to add form fields dynamically from a response to a user event? It’s easy to do with Reactive Forms and FormArray.FormArray is a bit like FormGroup and it’s used in a very similar way, the difference being that it’s used as an array that wraps around an arbitrary amount of FormControl, FormGroup or even other FormArray instances.

Comments
  • Why are you doing this?
  • @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?
  • FormArray are index based you should acces using at method, like this array2.at(0).setValue(['']);
  • @Chellappan Yes, I understand and appreciate this fact fully. Do you have an answer?
  • try this array2.reset(['','''])
  • I wanted to edit the FormArray in-place. But this is nice if you don't have that requirement.
  • How do I make a function out of this to solve problems in my app?
  • I improve (I hope) your function in a new answer