Reputation: 3223
I have this code for a textarea, it is working great with the ngModel and updating live, however i would like my-custom-directive to know when the model bound to this textarea is changing.
How can i detect a model change for this textarea in the my-custom-directive?
<textarea my-custom-directive class="green" [(ngModel)]="customertext"></textarea>
Upvotes: 17
Views: 35344
Reputation: 7654
Pass the model
(detecting change on) as Input()
to directive and use ngOnChange()
like below.
import {Directive, ElementRef, Input, OnChanges} from '@angular/core';
@Directive({
selector: '[myCustomDirective]'
})
export class MyCustomDirective implements OnChanges {
@Input() myCustomDirective: any;
constructor(private el: ElementRef) {}
ngOnChanges(changes) {
if (changes.myCustomDirective) {
console.log('chnaged');
}
}
}
Here is how you can pass the model as input to the directive:
<textarea [myCustomDirective]="customertext" class="green" [(ngModel)]="customertext"></textarea>
Upvotes: 2
Reputation: 2236
There are multiple ways to detect changes, it also depends on the ChangeDetectionStrategy defined in the Component.
To be helpful, I'll simplify the answer and focus on the Reactive Extensions (Rx) way of interacting with the ngModel directive, simply because it super powerful and has the best integration with angular 2 change detection engine, performance wise.
First, we need to emphasise that NgModel is bound to the value of the Element and all changes are propagated via an EventEmitter which you subscribe to. The EventEmitter is an Angular2 type that is a Rx Subject which is basically an object that can "transmit" (emit) events/data and also can react to data "received". So in Rx land its an Observable and an Observer.
Also worth mentioning is the "ngOnChanges" method on ngModel that receives handler for handling change events, but we will focus on the Rx approach.
So, we need to gain access to that Observable and subscribe to it, great!
To gain access we use dependancy injection, simply by declaring that we want to get NgModel in the constructor of our directive.
Now we also need to be careful, we need to make sure we do get that NgModel otherwise we will have errors.
We can do it silently, by creating a directive selector that enforces an NgModel element, like: 'selector': '[myCustomDirective][ngModel]', now if ngModel is not part of the element it won't match our directive's selector.
Or, we can make noise and not go out silently, we mark the dependency injection as @Optional and if its null we can throw a clear exception saying whats missing. If we won't mark it as @Optional angular will throw a generic message about a missing dependency, its ok but not good.
Now, an example:
import {Directive, Optional} from 'angular2/core';
import {NgModel}from 'angular2/common';
@Directive({
selector: '[myCustomDirective][ngModel]'
})
export class MyCustomDirective {
constructor(@Optional() public model: NgModel) {
if (!model)
throw new Error ("myCustomDirective requires ngModel.");
// this code should be in a designated functions...
// register to incoming changes to the model
// we can also transform values..., for example take only distinct values...
// this.model.update.distinctUntilChanged()...
let subscription = this.model.update.subscribe((newValue) => {
// Changes happen here, do something with them...
// note that the value of this.model is still the old value
// The local parameter "newValue" holds the new value.
});
// We can also change the value of the model:
this.model.update.emit('something...');
}
}
Hope that helped.
Upvotes: 6
Reputation: 111
try add to your directive:
@HostListener('input') onInput() {
console.log('on input');
}
Upvotes: 10
Reputation: 4274
There are different approaches to detect changes in Agnular2, and according to the conditions you're using them, they will have upside and downsides.
Angular lets you bind all the standard events to elements including change
. so in this case as a form element you can simply use:
<textarea my-custom-directive class="green" [(ngModel)]="customertext" (change)="doSomething($event)"></textarea>
The event will be native JavaScript event, so you can get the originalEvent
, target
, and also to get the change property and its value:
doSomething (e) {
let whatChanged = e.target.getAttribute('ng-reflect-name');
let newValue = this[whatChanged];
console.log(whatChanged, newValue);
}
This works well specially when you want to watch for changes in all/some of elements in a form. using hash-referencing is also pretty straight forward is this approach.
Upvotes: 4
Reputation: 657376
Update
@Directive({
selector: 'xyz',
host: {'(ngModelChange)': 'doSomething($event)'}
})
export class Xyz {
doSomething(event){... }
}
Original
<textarea my-custom-directive class="green" [(ngModel)]="customertext"
(ngModelChange)="doSomething($event) "></textarea>
[(ngModel)]="customertext"
is the short form of
[ngModel]="customertext" (ngModelChange)="customertext=$event"
Upvotes: 10