Home > database >  How to call a method every time a property is changed through two-way binding
How to call a method every time a property is changed through two-way binding

Time:05-04

I've an angular application, with the following component code

export class AppComponent {

  length: number = 200;
  width: number = 150;
  height: number = 100;
  margin: number = 5;
  fingerSize: number = 10;
  thickness: number = 3;

  doSomethingOnChange(){
    //...
  }
}

Each property is bound with 2-way binding:

<div style="display: flex">
  <div>
    <span>Width</span>
    <input type="number" [(ngModel)]="width" />
  </div>
  <div>
    <span>Length</span>
    <input type="number" [(ngModel)]="length" />
  </div>
  <div>
    <span>Height</span>
    <input type="number" [(ngModel)]="height" />
  </div>
  <div>
    <span>Thickness</span>
    <input type="number" [(ngModel)]="thickness" />
  </div>
  <div>
    <span>Finger size</span>
    <input type="number" [(ngModel)]="fingerSize" />
  </div>
  <div>
    <span>Margin</span>
    <input type="number" [(ngModel)]="margin" />
  </div>
</div>

I would like to call my method doSomethingOnChange() every time the user make any changes to those values. Is there a way?

I tried to implement the onChanges interface but the method was still not called on updates. Any idea of what I'm missing?

CodePudding user response:

You can use getter/setter to intercept interactions with given fields. ngOnChanges will be called only for mutations of @Input/@Output fields

export class AppComponent {

  _length: number = 200;
  get length(){
     doSomethingOnLengthRead();
     return this._length;
  }
  set length(v:number){
     doSomethingOnLengthWrite(v);
     this._length=v;
  }
}

alot of boilerplate will be generated, but will do the trick.

CodePudding user response:

Just add (change)="doSomethingOnChange()" to each input.

<input type="number" [(ngModel)]="width" (change)="doSomethingOnChange()" />

This will fire the event once the value is committed to the model.

OR , if you want to trigger every time user interacts with the input, you could do instead:

<input type="number" [ngModel]="width" (ngModelChange)="doSomethingOnChange()"/>

CodePudding user response:

An alternative is to add (ngModelChange)="doSomethingOnChange($event)" to your form element. From the NgModel docs:

Event emitter for producing the ngModelChange event after the view model updates.

Your HTML might then look something like:

<div style="display: flex">
  <div>
    <span>Width</span>
    <input type="number" [ngModel]="width" (ngModelChange)="handleWidthChange($event)" />
  </div>
  <div>
    <span>Length</span>
    <input type="number" [ngModel]="length" (ngModelChange)="handleLengthChange($event)" />
  </div>
  <div>
    <span>Height</span>
    <input type="number" [ngModel]="height" (ngModelChange)="handleHeightChange($event)" />
  </div>
  <div>
    <span>Thickness</span>
    <input type="number" [ngModel]="thickness" (ngModelChange)="handleThicknessChange($event)" />
  </div>
  <div>
    <span>Finger size</span>
    <input type="number" [ngModel]="fingerSize" (ngModelChange)="handleFingerSizeChange($event)" />
  </div>
  <div>
    <span>Margin</span>
    <input type="number" [ngModel]="margin" (ngModelChange)="handleMarginChange($event)" />
  </div>
</div>

You'll just want to make sure that you are setting the value that is then bound back to your form element. Ex:

handleWidthChange(event: any) {
  this.width = event;
}

Should result in less boilerplate code than the getter/setter method if using template-driven forms.

CodePudding user response:

Have you tried <input type="number" [(ngModel)]="width" (change)="doSomethingOnChange($event)" /> ?

or you could use ReactiveFormsModule by creating a FormGroup and subscribe the form. For example,

myForm: FormGroup = new FormGroup({
    width: new FormControl(this.width, [Validators.required]),
    height: new FormControl(this.height, [Validators.required]),
    ..........
  });

this.myForm.valueChanges.subscribe(v => { doSomethingOnChange() })

<div [formGroup]="myForm">
    <input type="number" formControlName="width"/>
    ......
</div>
  • Related