31

I have a component with a few inputs that I'd like to be notified when it changes. I currently have it working by implementing ngOnChanges and figuring out which input was changed. However, I would prefer set my input declaration to @Input('select-values') selectValues:Observable<any>. This would allow me to subscribe to any new changes that occur in a much cleaner fashion.

ngOnInit() {
    this.selectValues.subscribe(() => console.log('yay!'));
}

Issue with this is that I'm getting exception TypeError: this.selectValues.subscribe is not a function.

Just found out that this also works – Component Interaction. Intercept input property changes with a setter.

dhilt
  • 18,707
  • 8
  • 70
  • 85
self.
  • 1,612
  • 4
  • 18
  • 35
  • 4
    Isn't this question about changes to input properties, not form inputs? If so, then I don't think either answer should be accepted. I think the answer should be the link you provided to the cookbook. – Mark Rajcok Apr 08 '16 at 21:52

3 Answers3

31

You can wrap them in a form and listen to changes

this.myForm = fb.group({  
  'sku':  ['', Validators.required]  
});

this.sku = this.myForm.controls['sku'];

this.sku.valueChanges.subscribe(  
  (value: string) => {  
    console.log('sku changed to: ', value);  
  }
);

this.myForm.valueChanges.subscribe(  
  (value: string) => {  
    console.log('form changed to: ', value);  
  }
);

http://blog.ng-book.com/the-ultimate-guide-to-forms-in-angular-2/

or

@Component({
   ...,
   template: '<input (change)="onChange($event.target.value)">'
})
class MyComponent {
  this.inputChange =new Subject();

  onChange(e) {
    this.inputChange.next(e);
  }
}

See also this issue open https://github.com/angular/angular/issues/4062

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
22

In fact, it's not possible to directly register against observables associated with events for DOM elements. You need to reference a DOM element directly and use then the fromEvent method of Observable.

Here is a sample:

@Component({
  (...)
  template: `
    <input #input />
  `
})
export class SomeComponent {
  @ViewChild('input')
  input:ElementRef;

  ngAfterViewInit() {
    var eventObservable = Observable.fromEvent(
              this.input.nativeElement, 'keyup');
  }
}

This issue could interest you as well:

That said you can leverage form controls to be notified when input value is updated. The valueChanges attribute of the control could be passed as input of the sub component.

@Component({
  (...)
  template: `
    <input [ngFormControl]='ctrl'/>
    <child-component [select-values]="ctrl.valueChanges"></child-component>
  `
})
export class SomeComponent {
  constructor() {
    this.ctrl = new Control();

    this.ctrl.valueChanges.subscribe(...);
  }
}
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • Newer issue superseding the old one: Support cold event streams in template (on hold): https://github.com/angular/angular/issues/13248 – charlie_pl Apr 23 '17 at 15:15
14

If you just need to observe a single input, you can quickly do:

in component:

ngOnInit() {
  this.searchField = new FormControl();
  this.searchField.valueChanges.subscribe(term => {
    console.log('searching for', term);
  });
}

in html:

<input type="search" [formControl]="searchField">

and maybe unsubscribe in ngOnDestroy.

mnewmedia
  • 485
  • 1
  • 6
  • 20
  • 2
    Oh snap thats exactly what i was looking for. Why hasn't this been mentioned much anywhere else when its so easy. thanks! – Dustin Silk Jul 11 '18 at 11:09
  • 3
    It's worth noting that you can't use a FormControl by itself. You have to put it inside a FormGroup. – mwilson Oct 14 '19 at 20:30