Reputation: 2337
Currently I have an Angular form like
<form>
Name: <input name="name" [(ngModel)]="data.name"><br>
Age: <input name="age" [(ngModel)]="data.age"><br>
City: <input name="city" [(ngModel)]="data.city"><br>
<button (click)='update()'>
</form>
Upon update I'd like to be able to have an object containing just changed fields. Just for quick and dirty solution I achieved my need by using Proxy
which looks more or less like below (with assigning component's data
to be changeTracker.proxy
):
class ChangeTracker {
proxy;
changed;
constructor(initialTarget = {}) {
this.changed = {};
this.proxy = new Proxy(initialTarget, {
get(target, key) {
return target[key];
},
set(target, key, val) {
target[key] = val;
this.changed[key] = val;
return true;
}
});
}
}
So eventually I can do i.e. REST PUT
http.put('/some/api/type', { objectId, ...changeTracker.changed });
I was not able to find any SO answer or external tutorial addressing directly the problem of getting set of changes from the form. Wonder what's the Angular way of achieving this - the best would be to avoid unnecessary boilerplate yet looking for any reasonable solution.
Upvotes: 0
Views: 62
Reputation: 2337
Thanks to Fateh Mohamed answer I got convinced on not so verbose way of using FormBuilder
with the proposed template
<form id="personFormId" [formGroup]="personForm">
Name: <input name="name" formControlName="nameControl"><br>
Age: <input name="age" formControlName="ageControl"><br>
City: <input name="city" formControlName="cityControl"><br>
<button (click)='update()'>
</form>
and achieve the main goal which is to collect the all the changes (and only changes) into one object. Remember the component has to implements OnInit
and below is still simplified key part of the solution.
ngOnInit() {
this.changedData = {};
this.form = this.formBuilder.group({
name: [''],
age: [''],
city: ['']
});
// for ngrx < 6.x mergeMap should be used directly without a pipe
from(Object.entries<AbstractControl>(this.form.controls))
.pipe(mergeMap(([key, control]) =>
control.valueChanges.map(value => ({ [key]: value }))))
.subscribe(change => {
this.changedData = {
...this.changedData,
...change
}
});
}
setFormData(data) {
this.form.patchValue(data);
this.changedData = {};
}
update() {
http.put('/some/api/type', { id, ...this.changedData });
this.changedData = {};
}
Upvotes: 0
Reputation: 21377
here is a solution with reactive forms, one of nice things about model driven forms is that it has valueChanges, you can subscribe to forms changes
<form id="personFormId" [formGroup]="personForm">
Name: <input name="name" formControlName="nameControl"><br>
Age: <input name="age" formControlName="ageControl"><br>
City: <input name="city" formControlName="cityControl"><br>
<button (click)='update()'>
</form>
ts file
public personForm: FormGroup;
constructor(private router: Router, private _fb: FormBuilder) { }
ngOnInit() {
this.personForm= this._fb.group({
nameControl: ['', [<any>Validators.required]],
ageControl: ['', [<any>Validators.required]],
cityControl: ['', [<any>Validators.required]]
});
this.onFormChanges();
}
form.valueChanges will return an observable
onFormChanges() {
this.personForm.valueChanges.distinct().subscribe(val => {
// do your control here
});
}
Upvotes: 1