0

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.

1
  • 1
    you can use reactive forms, it has valueChanges, you can subscribe to it when the form changes and get the new data Commented Jun 5, 2018 at 21:51

2 Answers 2

1

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
        });
  }
Sign up to request clarification or add additional context in comments.

3 Comments

Awesome, thanks! I already started to skim reactive forms' docs. Just to be clear - it's still on me to collect/combine all the changes to get one object containing all the changed fields correct? if so thats ok, I know how to do this with rxjs just don't want to write what's already given. I guess in AngularJS 1.x it was possible to get the 'dirty` fields.
here you will have the form final value with all fields each time one of them is changed, you can console log val to see its content . you can listen to changes of a specific form controls too if you need it: this.personForm.get('nameControl').valueChanges.subscribe(...)
I've upvoted as for pointing the right direction yet the form.valueChanges is still far from the goal of getting an object containing changed fields.
0

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 = {};
}

Comments

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.