I've encountered a strange bug with Angular reactive forms. I am using a FormArray to create a form where fields can be added or deleted. I also want to be able to reset the form whereby the values and number of form inputs goes back to the original amount. I'm currently able to instantiate the form, add fields and delete them fine but when I press reset the function I've created which firsts empties the FormArray and then recreates the fields using the same process as I used to set up the form initially, the value aren't be displayed properly. I'm not sure why this is happening, perhaps it's to do with the formControlNames used to bind the form in the html?
Does anyone know what's causing the issue or what's the proper way to reset the form values?
I've created a stackblitz here: https://stackblitz.com/edit/angular-reactive-formarray-bug
Here's my component code.
import {
Component, ElementRef
} from '@angular/core';
import { FormBuilder, FormGroup, FormArray, FormControl } from '@angular/forms';
import { ContentTemplate, ContentTemplateEntry, NewContentEntry } from './models';
import {Observable, Subscription, of} from 'rxjs';
import data from './template-data.json';
@Component({
selector: 'material-app',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss']
})
export class AppComponent {
public templateForm: FormGroup;
public contentTemplate$: Observable<ContentTemplate>;
public activeTemplate: ContentTemplate;
public templateSub: Subscription;
public entries: ContentTemplateEntry[];
get templateEntries(): FormArray {
return <FormArray>this.templateForm.get('entries');
}
constructor(
private fb: FormBuilder
) {
this.contentTemplate$ = of(data)
}
ngOnInit(): void {
this.templateSub = this.contentTemplate$.subscribe((template: ContentTemplate) => {
this.activeTemplate = {...template};
this.entries = [...template.entries];
this.templateForm = this.fb.group({
entries: this.fb.array([])
});
this._processEntries(this.entries);
});
}
ngOnDestroy(): void {
this.templateSub.unsubscribe();
}
private _buildEntry(entry: ContentTemplateEntry) {
const g = this.fb.group({
id: {value: entry.id},
title: {value: entry.title, disabled: entry.isRequired},
orderNumber: {value: entry.orderNumber},
type: {value: entry.type}
});
return g;
}
private _processEntries(entries: ContentTemplateEntry[]) {
entries.forEach((e, i) => {
this.templateEntries.push(this._buildEntry(e));
});
}
private _getOrderNumber(): number {
return this.templateEntries.length + 1;
}
private _removeItemsFromEntries(): void {
while (this.templateEntries.length > 0) {
this.templateEntries.removeAt(0);
}
}
// reinstantiate using the same approach as before
public resetForm() {
this._removeItemsFromEntries();
this._processEntries(this.entries);
}
public removeEntry(id: number) {
this.templateEntries.removeAt(id);
}
public addEntry() {
this.templateEntries.push(
this._buildEntry(new NewContentEntry({
orderNumber: this._getOrderNumber()
}))
);
}
public save() {
console.log('save triggered');
}
}