62

I'm trying to detect when the value of an input changed in a directive. I have the following directive:

    import { ElementRef, Directive, Renderer} from '@angular/core';

    @Directive({
        selector: '[number]',
        host: {"(input)": 'onInputChange($event)'}
    })

    export class Number {

        constructor(private element: ElementRef, private renderer: Renderer){

        }
        onInputChange(event){
            console.log('test');
        }
    }

The problem in this directive is that it detects only when there is an input and not when the value changes programatically. I use reacive form and sometimes I set the value with the patchValue() function. How can I do so the change function gets triggered?

5 Answers 5

85

You need to make an input property of input and then use the ngOnChanges hook to tell when the input property changes.

@Directive({
    selector: '[number]'
})
export class NumberDirective implements OnChanges {
    @Input() public number: any;
    @Input() public input: any;

    ngOnChanges(changes: SimpleChanges){
      if(changes.input){
        console.log('input changed');
      }
    }
}

Plunkr

Stackblitz

Sign up to request clarification or add additional context in comments.

2 Comments

ngOnChanges(changes : SimpleChanges)
This requires an input directive, it should use host listener without any dependencies
16

There is a better way to detect when an Input property changes, it's considered a best practice and it's also used in the *ngIf implementation.

You just need to postpone the keyword set after Input(), in this way you combine the @Input() decorator with a setter, and it gets called all the times the value changes.

_rows: number;
@Input() set rows(value: number) {
  if (this.value < 0) {
    console.warn('The number of rows must be positive, but got ' + this.value);
    console.warn('Setting default: 2');
    this._rows = 2;
    return;
  }
 
  this._rows = value;
}

If you want to compare the new value with the previous value, you must store the variable in a class property, and retrieve it the second time the method got called:

private previousValue: any = T;

@Input() set myInputName(value: T) {
  console.log(`Previous value was: ${this.previousValue}`);
  console.log(`New value is: ${value}`);
  this.previousValue = value;  
}

5 Comments

You are not answering to the question asked
@MattewEon Wtf? This solves perfectly the problems, since it detects when the input is changed programmatically
The goal isn't to add a new @Input property to the component, he want to detect the change on the ngModel property
Good approach using latest Angular 7 @Input style.
If the name of the @input variable is the same as the directive, then you can get a watch on the ngModel. e.g. @Input() set number(value: T) {...} Usage example would be <input number="{{someModel}}">
9

As of Angular 9+ I can verify that the snippet below is the correct solution. It's best to avoid ngOnChanges whenever possible for several reasons and use the correct host listener event instead. The below example will get the current input value on keyup. You can easily customize this code snippet for whatever you need on an input directive.

  @Directive({
      selector: '[appMyDirective]',
  })
  export class MyDirective {
    // Add whatever event you want to track here
    @HostListener('keyup', ['$event']) public onKeyup(event: KeyboardEvent): void {
      const value = (event.target as HTMLInputElement).value;
      console.log(value);
    }
  }

3 Comments

This works well, just remember that HostListener will trigger a change detection, whether or not you're actually handling the event / changing anything. This might impact performance and still applies even if all components use ChangeDetectionStrategy.OnPush.
This only captures on keyup events, not programmatically updating the input value.
This assumes that the directive input value changes due to a keyboard event. This is not necessarily the case.
6

You can also use HostListener. For more information about HostListener, you can go through this link. Here is the code.

import {Directive, ElementRef, HostListener} from '@angular/core';


@Directive({
       selector: '[number]'
 })

export class NumberDirective {

    @Input() public number: any;
    @Input() public input: any;

    constructor(private el: ElementRef) {}

    @HostListener('change') ngOnChanges() {
        console.log('test');
    }

}

2 Comments

'change' not work, 'mouseenter', 'mouseleave' from angular sample work. Please, test your answers.
'change' works for select menus, at least (as does 'ngModelChange', and you can get the new value by injecting the $event var: @HostListener('ngModelChange', ['$event']) public doStuff(value) { ... })
0

To listen to value changes of an input in Angular, use the HostListener decorator in a directive:

@Directive({
  selector: '[appYourDirective]'
})
export class YourDirective {

  constructor(private el: ElementRef) {
  }

  @HostListener('input') logChange() {
    console.log(this.el.nativeElement.value);
  }
}

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.