@HostListener('change') not working - Angular2 RC1
Asked Answered
H

3

6

I have created an attribute directive myOptional meant to be used on inputs in a form, and its purpose is to indicate that certain fields are optional. This is done by adding a class to the input and then the optional text is shown using css pseudo element ::after.

The "Optional" label is meant to be displayed only when the input's value is empty, and the focus is elsewhere.

So, when the directive is initialized, we add the class to the input

ngOnInit() {
    this.addOptionalClass();
}

On focus, we remove the class and therefore, the Optional label

@HostListener('focus') onfocus() {
    this.removeOptionalClass();
}

On blur, if the input's value is still empty, we shown the label

@HostListener('blur') onblur() {
    if (this.isInputElement()) {
        let inputValue = (<HTMLInputElement>this.el).value;
        if (inputValue === '') this.addOptionalClass();
    }
}

So far, so good. The problem occurs when the input's value is set by updating the control in the form. In that case, when the input's value is updated and not empty, I'd like to remove the class.

I assumed I'd be able to attach an event listener to the onchange event, but the code below is not triggered at all. I even tried modifying the input's value using document.getElementBydId as well, with no success.

@HostListener('change') onchange() {
    console.log((<HTMLInputElement>this.el).value);
}

Hope I made myself clear. Any help would be greatly appreciated.

Hothouse answered 20/9, 2016 at 23:22 Comment(0)
H
3

I finally solved this by passing the control's value as an Input. Then, on the ngOnChanges lifecycle hook, I evaluate the input and add/remove the class based on whether it has value.

export class OptionalDirective implements OnInit, OnChanges {
    @Input('myOptional') private controlValue: string;

    constructor(private elementRef: ElementRef) {
        this.el = elementRef.nativeElement;
    }

    ngOnChanges(changes: { [propName: string]: SimpleChange }) {
        if (changes['controlValue'] &&
            !changes['controlValue'].isFirstChange()
            && changes['controlValue'].currentValue !== changes['controlValue'].previousValue) {
            if (this.controlValue) this.removeOptionalClass();
            else this.addOptionalClass();
        }
    }
}
Hothouse answered 22/9, 2016 at 12:27 Comment(2)
Just for info, @Input properties always should be public.Paraldehyde
How you pass the controlValue from the element component to the directive?Literary
S
2

You are right, 'change' will not work. Cant really say whay, but found this on the Github repo: https://github.com/angular/angular/issues/4593

See this plunker how to do it using keyup: https://plnkr.co/edit/kIHogCxQaPPRQyqDVqTE?p=preview

import {Component, NgModule, HostListener, Directive} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Directive({
  selector: 'input[myOptional]'
})
export class OptionalDirective {

  // THIS WILL BE FIRED IF SOMEONE CHANGES THE INPUT
  @HostListener('keyup', ['$event'])
  inputChanged(event) {
    if (event.target.value) {
      console.log('not empty!');
      // REMOVE YOUR CLASS HERE
    }
    else // ADD YOUR CLASS HERE
  }
}

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <input myOptional />
    </div>
  `,
})
export class App {
  constructor() {
    this.name = 'Angular2'
  }
}

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App, OptionalDirective ],
  bootstrap: [ App ]
})
export class AppModule {}
Stgermain answered 21/9, 2016 at 4:33 Comment(5)
Thanks for the prompt response. However, this is not working. Populating the input's value does not fire the inputChanged methodHothouse
I don't want to fire any event.. see my updated answer with additional comments. Or maybe I don't get your real problem ..Stgermain
The input's value is set programmatically by updating the form control. (<Control>this.form.controls[c]).updateValue(obj[c]). I'd like the optional class to be removed when this happens. However, this is not triggering the inputChanged method you posted, nor the onchange I posted above.Hothouse
Ah, I see.. thought user should input something.. That's true, in this case keyup isn't helpful.. let me take a look for another solution! :)Stgermain
Thank you for your help! Sorry I did not make myself clear. Upvoted the answer though since someone might find it usefulHothouse
E
2

To detect changes of an input's value in a Directive, use ngModelChange:

@HostListener('ngModelChange', ['$event']) onChange(event) {
    // "event" will be the value of the input
    console.log(event);
}
Emmert answered 5/7, 2021 at 19:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.