130

I am attempting to detect a change on ngModel in a <select> tag. In Angular 1.x, we might solve this with a $watch on ngModel, or by using ngChange, but I've yet to understand how to detect a change to ngModel in Angular 2.

Full Example: http://plnkr.co/edit/9c9oKH1tjDDb67zdKmr9?p=info

import {Component, View, Input, } from 'angular2/core';
import {FORM_DIRECTIVES} from 'angular2/common';

@Component({
    selector: 'my-dropdown'
})
@View({
    directives: [FORM_DIRECTIVES],
    template: `
        <select [ngModel]="selection" (ngModelChange)="onChange($event, selection)" >
            <option *ngFor="#option of options">{{option}}</option>
        </select>
        {{selection}}
    `
})
export class MyDropdown {
    @Input() options;

    selection = 'Dog';

    ngOnInit() {
        console.log('These were the options passed in: ' + this.options);
  }

  onChange(event) {
    if (this.selection === event) return;
    this.selection = event;
    console.log(this.selection);
  }

}

As we can see, if we select a different value from the dropdown, our ngModel changes, and the interpolated expression in the view reflects this.

How do I get notified of this change in my class/controller?

Nimantha
  • 6,405
  • 6
  • 28
  • 69
lux
  • 8,315
  • 7
  • 36
  • 49
  • 1
    you might want to keep some of the extra comments in check; you don't want this question to be flagged as a rant in disguise. http://stackoverflow.com/help/dont-ask. – Claies Dec 21 '15 at 22:26

2 Answers2

312

Update:

Separate the event and property bindings:

<select [ngModel]="selectedItem" (ngModelChange)="onChange($event)">
onChange(newValue) {
    console.log(newValue);
    this.selectedItem = newValue;  // don't forget to update the model here
    // ... do other stuff here ...
}

You could also use

<select [(ngModel)]="selectedItem" (ngModelChange)="onChange($event)">

and then you wouldn't have to update the model in the event handler, but I believe this causes two events to fire, so it is probably less efficient.


Old answer, before they fixed a bug in beta.1:

Create a local template variable and attach a (change) event:

<select [(ngModel)]="selectedItem" #item (change)="onChange(item.value)">

plunker

See also How can I get new selection in "select" in Angular 2?

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • 1
    So what's the point of `ngModel` if I'm just binding a new variable called `item`? Isn't the point of wrapping `ngModel` in parenthesis to acquire event listeners, so why are we introducing a new variable? – lux Dec 21 '15 at 22:46
  • 2
    @lux, yeah good question. `selectedItem` is our bound data, which NgModel updates automatically for us, but... it doesn't notify us of changes, which often is good enough (views and such will update), but obviously this is not good enough for your use case. In the other SO question I referenced, I describe how I tried to use `(ngModelChange)` to get notified of changes, but it gets called twice for each change. I don't know if that is a bug or not. Anyway, adding a `(change)` event binding seems to solve the issue. – Mark Rajcok Dec 21 '15 at 22:56
  • Also, I updated the plunker which shows that `selectedItem` is not updated when `onChange()` fires, hence it seems we need that local template variable. – Mark Rajcok Dec 21 '15 at 22:57
  • @lux the `#` or `#item` in our case is a **local** reference. Hence why we're able to do `item.change` there. – Mark Pieszak - Trilon.io Dec 21 '15 at 22:58
  • @lux, I already described the way to hook in: bind to the `ngModelChange` custom event. The problem is, with ` – Mark Rajcok Dec 21 '15 at 23:21
  • @lux, I think you should add your solution as another answer. – Mark Rajcok Dec 22 '15 at 02:00
  • @lux If you decouples the event binding from the property assignment, what is the job of `[ngModel]="selection"`? What I don't understand is that once you make them apart like `[ngModel]="someProperty"` and `(ngModelChange)="handler"`, `ngModelChange` event still fires with the value of what user chose but `someProperty` doesn't change unless you do it yourself as @Mark Rajcok mentioned earlier. – DongBin Kim Mar 19 '18 at 05:42
19

I have stumbled across this question and I will submit my answer that I used and worked pretty well. I had a search box that filtered and array of objects and on my search box I used the (ngModelChange)="onChange($event)"

in my .html

<input type="text" [(ngModel)]="searchText" (ngModelChange)="reSearch(newValue)" placeholder="Search">

then in my component.ts

reSearch(newValue: string) {
    //this.searchText would equal the new value
    //handle my filtering with the new value
}
Logan H
  • 3,383
  • 2
  • 33
  • 50
  • 6
    Just FYI, when binding to `ngModelChange`, `$event` is not a DOM [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event). Rather it is the current value of the form element, which is a string for an input element. – Mark Rajcok Apr 25 '16 at 21:33
  • @MarkRajcok can you please point me to documentation for this, so i can share with the rest of my dev team? – Neil S Feb 17 '17 at 19:48
  • 1
    @NeilS, the closest is https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ngModel – Mark Rajcok Feb 17 '17 at 22:00