18

Can someone please tell me what is the best practice for comparing ngModel old and new value?

In angular 1:

$scope.$watch('someProperty', funciton(oldVal, newVal){
    // code goes here
})

I am asking this because (ngModelChange) never brings me the oldVal , only the newVal.

In my case, I am using ngModel in a <select> tag and compare the old selection with the new one:

<select [(ngModel)]="current" (ngModelChange)="onModelChange($event)">
     <option *ngFor="let item of myArray" [ngValue]="item">{{item.name}} </option>
</select>
Ron Avraham
  • 478
  • 2
  • 7
  • 18

3 Answers3

19

This might work

(ngModelChange)="onModelChange(oldVal, $event); oldVal = $event;"

or

(ngModelChange)="onModelChange($event)"
oldValue:string;
onModelChange(event) {
  if(this.oldValue != event) {
    ...
  }
  this.oldValue = event;
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • I tried this but the oldVal param is undefined. I am using this comparation in a – Ron Avraham Jan 09 '17 at 09:12
  • Just declare a variable with this name in the components class. I added it to my code (in the 2nd example) – Günter Zöchbauer Jan 09 '17 at 09:13
  • 2
    Ok, i get it now. but this solution seems to be very manually, are you sure that is the best practice? there is nothing delivered by angular 2 for getting the oldVal? – Ron Avraham Jan 09 '17 at 09:22
  • No, Angular only provides `$event` or if you use `[ngModel]="myValue"` updates the `myValue` property with the new value. If you use `[myProp]="someValue"` binding to an `@Input() myProp;`, then when the binding is updated `ngOnChanges(changes)`, `changes` contains the old value as well, but that doesn't apply to your situation, it is only for binding from parent to child. – Günter Zöchbauer Jan 09 '17 at 09:24
  • In my case, select value is already set, so the value of `this.oldValue` is `undefined`, because the onModelChange would be run for the first time. Is there a workaround for this as well? – Islam Murtazaev Nov 12 '19 at 07:39
16

Just for the future

we need to observe that [(ngModel)]="hero.name" is just a short-cut that can be de-sugared to: [ngModel]="hero.name" (ngModelChange)="hero.name = $event".

So if we de-sugar code we would end up with:

<select (ngModelChange)="onModelChange()" [ngModel]="hero.name" (ngModelChange)="hero.name = $event">

or

<[ngModel]="hero.name" (ngModelChange)="hero.name = $event" select (ngModelChange)="onModelChange()">

If you inspect the above code you will notice that we end up with 2 ngModelChange events and those need to be executed in some order.

Summing up: If you place ngModelChange before ngModel, you get the $event as the new value, but your model object still holds previous value. If you place it after ngModel, the model will already have the new value.

SOURCE

Sample stack blitz: https://stackblitz.com/edit/angular-ivy-zlhwex

Davy
  • 6,295
  • 5
  • 27
  • 38
Disaster
  • 861
  • 8
  • 6
  • Wow, I had no idea that these would literally fire sequentially based on their position in the template. A great help. Here's hoping they don't standardize the execution order for this at a later time... – Cameron Forward Nov 16 '22 at 01:26
9

Example with input field...

<div *ngFor="let value of values">{{value}}
    <input [(ngModel)]="value" (focus)="old=value" (ngModelchange)="doSomething(old, value)">
</div>

doSomething(oldVal, newVal) {
    // some code
}
AT82
  • 71,416
  • 24
  • 140
  • 167
  • 1
    I am using this comparation in a – Ron Avraham Jan 09 '17 at 09:13