Remove all items from a FormArray in Angular
AngularAngular2 FormsAngular Problem Overview
I have a form array inside a FormBuilder and I am dynamically changing forms, i.e. on click load data from application 1 etc.
The issue I am having is that all the data loads in but the data in the FormArray stays and just concats the old items with new.
How do I clear that FormArray to only have the new items.
I've tried this
const control2 = <FormArray>this.registerForm.controls['other_Partners'];
control2.setValue([]);
but it doesn't work.
Any ideas?
ngOnInit(): void {
this.route.params.subscribe(params => {
if (params['id']) {
this.id = Number.parseInt(params['id']);
} else { this.id = null;}
});
if (this.id != null && this.id != NaN) {
alert(this.id);
this.editApplication();
this.getApplication(this.id);
} else {
this.newApplication();
}
}
onSelect(Editedapplication: Application) {
this.router.navigate(['/apply', Editedapplication.id]);
}
editApplication() {
this.registerForm = this.formBuilder.group({
id: null,
type_of_proposal: ['', Validators.required],
title: ['', [Validators.required, Validators.minLength(5)]],
lead_teaching_fellow: ['', [Validators.required, Validators.minLength(5)]],
description: ['', [Validators.required, Validators.minLength(5)]],
status: '',
userID: JSON.parse(localStorage.getItem('currentUser')).username,
contactEmail: JSON.parse(localStorage.getItem('currentUser')).email,
forename: JSON.parse(localStorage.getItem('currentUser')).firstname,
surname: JSON.parse(localStorage.getItem('currentUser')).surname,
line_manager_discussion: true,
document_url: '',
keywords: ['', [Validators.required, Validators.minLength(5)]],
financial_Details: this.formBuilder.group({
id: null,
buying_expertise_description: ['', [Validators.required, Validators.minLength(2)]],
buying_expertise_cost: ['', [Validators.required]],
buying_out_teaching_fellow_cost: ['', [Validators.required]],
buying_out_teaching_fellow_desc: ['', [Validators.required, Validators.minLength(2)]],
travel_desc: ['', [Validators.required, Validators.minLength(2)]],
travel_cost: ['', [Validators.required]],
conference_details_desc: ['', [Validators.required, Validators.minLength(2)]],
conference_details_cost: ['', [Validators.required]],
}),
partners: this.formBuilder.array([
//this.initEditPartner(),
//this.initEditPartner()
// this.initMultiplePartners(1)
]
),
other_Partners: this.formBuilder.array([
//this.initEditOther_Partners(),
])
});
}
getApplication(id) {
this.applicationService.getAppById(id, JSON.parse(localStorage.getItem('currentUser')).username)
.subscribe(Response => {
if (Response.json() == false) {
this.router.navigateByUrl('/');
} else {
this.application = Response.json();
for (var i = 0; i < this.application.partners.length;i++) {
this.addPartner();
}
for (var i = 0; i < this.application.other_Partners.length; i++) {
this.addOther_Partner();
}
this.getDisabledStatus(Response.json().status);
(<FormGroup>this.registerForm) .setValue(Response.json(), { onlySelf: true });
}
});
}
ngOnInit is not being called on click
Angular Solutions
Solution 1 - Angular
I had same problem. There are two ways to solve this issue.
Preserve subscription
You can manually clear each FormArray element by calling the removeAt(i)
function in a loop.
clearFormArray = (formArray: FormArray) => {
while (formArray.length !== 0) {
formArray.removeAt(0)
}
}
> The advantage to this approach is that any subscriptions on your formArray
, such as that registered with formArray.valueChanges
, will not be lost.
See the FormArray documentation for more information.
Cleaner method (but breaks subscription references)
You can replace whole FormArray with a new one.
clearFormArray = (formArray: FormArray) => {
formArray = this.formBuilder.array([]);
}
> This approach causes an issue if you're subscribed to the formArray.valueChanges
observable! If you replace the FromArray with a new array, you will lose the reference to the observable that you're subscribed to.
Solution 2 - Angular
Or you can simply clear the controls
this.myForm= {
name: new FormControl(""),
desc: new FormControl(""),
arr: new FormArray([])
}
Add something array
const arr = <FormArray>this.myForm.controls.arr;
arr.push(new FormControl("X"));
Clear the array
const arr = <FormArray>this.myForm.controls.arr;
arr.controls = [];
When you have multiple choices selected and clear, sometimes it doesn't update the view. A workaround is to add
arr.removeAt(0)
UPDATE
A more elegant solution to use form arrays is using a getter at the top of your class and then you can access it.
get inFormArray(): FormArray {
this.myForm.get('inFormArray') as FormArray;
}
And to use it in a template
<div *ngFor="let c of inFormArray; let i = index;" [formGroup]="i">
other tags...
</div>
Reset:
inFormArray.reset();
Push:
inFormArray.push(new FormGroup({}));
Remove value at index: 1
inFormArray.removeAt(1);
UPDATE 2:
Get partial object, get all errors as JSON and many other features, use the NaoFormsModule
Solution 3 - Angular
As of Angular 8+ you can use clear()
to remove all controls in the FormArray:
const arr = new FormArray([
new FormControl(),
new FormControl()
]);
console.log(arr.length); // 2
arr.clear();
console.log(arr.length); // 0
For previous versions the recommended way is:
while (arr.length) {
arr.removeAt(0);
}
Solution 4 - Angular
Angular 8
simply use clear()
method on formArrays :
(this.invoiceForm.controls['other_Partners'] as FormArray).clear();
Solution 5 - Angular
Warning!
The Angular v6.1.7 FormArray documentation says:
> To change the controls in the array, use the push, insert, or removeAt > methods in FormArray itself. These methods ensure the controls are > properly tracked in the form's hierarchy. Do not modify the array of > AbstractControls used to instantiate the FormArray directly, as that > result in strange and unexpected behavior such as broken change > detection.
Keep this in mind if you are using the splice
function directly on the controls
array as one of the answer suggested.
Use the removeAt
function.
while (formArray.length !== 0) {
formArray.removeAt(0)
}
Solution 6 - Angular
Angular v4.4 if you need to save the same reference to the instance of FormArray try this:
purgeForm(form: FormArray) {
while (0 !== form.length) {
form.removeAt(0);
}
}
Solution 7 - Angular
You can easily define a getter for your array and clear it as follows:
formGroup: FormGroup
constructor(private fb: FormBuilder) { }
ngOnInit() {
this.formGroup = this.fb.group({
sliders: this.fb.array([])
})
}
get sliderForms() {
return this.formGroup.get('sliders') as FormArray
}
clearAll() {
this.formGroup.reset()
this.sliderForms.clear()
}
Solution 8 - Angular
Use FormArray.clear() to remove all the elements of an array in a FormArray
Solution 9 - Angular
Simply
formArray.clear()
would be enough
Solution 10 - Angular
Update: Angular 8 finally got method to clear the Array FormArray.clear()
Solution 11 - Angular
Since Angular 8 you can use this.formArray.clear()
to clear all values in form array.
It's a simpler and more efficient alternative to removing all elements one by one
Solution 12 - Angular
Provided the data structure for what you will be replacing the information in the array with matches what is already there you can use patchValue
https://angular.io/docs/ts/latest/api/forms/index/FormArray-class.html#!#reset-anchor
> patchValue(value: any[], {onlySelf, emitEvent}?: {onlySelf?: boolean, > emitEvent?: boolean}) : void Patches the value of the FormArray. It > accepts an array that matches the structure of the control, and will > do its best to match the values to the correct controls in the group. > > It accepts both super-sets and sub-sets of the array without throwing > an error.
const arr = new FormArray([
new FormControl(),
new FormControl()
]);
console.log(arr.value); // [null, null]
arr.patchValue(['Nancy']);
console.log(arr.value); // ['Nancy', null]
Alternatively you could use reset
> reset(value?: any, {onlySelf, emitEvent}?: {onlySelf?: boolean, > emitEvent?: boolean}) : void Resets the FormArray. This means by > default: > > The array and all descendants are marked pristine The array and all > descendants are marked untouched The value of all descendants will be > null or null maps You can also reset to a specific form state by > passing in an array of states that matches the structure of the > control. The state can be a standalone value or a form state object > with both a value and a disabled status. >
this.arr.reset(['name', 'last name']);
console.log(this.arr.value); // ['name', 'last name']
> > OR >
this.arr.reset([ {value: 'name', disabled: true}, 'last' ]);
console.log(this.arr.value); // ['name', 'last name']
console.log(this.arr.get(0).status); // 'DISABLED'
Here's a forked Plunker demo from some earlier work of mine demoing a very simple utilization of each.
Solution 13 - Angular
I never tried using formArray, I have always worked with FormGroup, and you can remove all controls using:
Object.keys(this.formGroup.controls).forEach(key => {
this.formGroup.removeControl(key);
});
being formGroup an instance of FormGroup.
Solution 14 - Angular
To keep the code clean I have created the following extension method for anyone using Angular 7 and below. This can also be used to extend any other functionality of Reactive Forms.
import { FormArray } from '@angular/forms';
declare module '@angular/forms/src/model' {
interface FormArray {
clearArray: () => FormArray;
}
}
FormArray.prototype.clearArray = function () {
const _self = this as FormArray;
_self.controls = [];
_self.setValue([]);
_self.updateValueAndValidity();
return _self;
}
Solution 15 - Angular
(this.questionForm.controls['answers'] as FormArray).clear();
Solution 16 - Angular
I am very late but I found some other way where you don't need to have loops. you can reset array by setting array control to empty.
Below code will reset your array.
this.form.setControl('name', this.fb.array([]))
Solution 17 - Angular
While loop will take long time to delete all items if array has 100's of items. You can empty both controls and value properties of FormArray like below.
clearFormArray = (formArray: FormArray) => { formArray.controls = []; formArray.setValue([]); }
Solution 18 - Angular
If you are using Angular 7 or a previous version and you dont' have access to the clear() method, there is a way to achieve that without doing any loop:
yourFormGroup.controls.youFormArrayName = new FormArray([]);