How to observe input element changes in ng-content

How to call parent component’s function when child component observed input changes?

The below is HTML structure.

# app.comopnent.html
<form>
  <textbox>
    <input type="text">
  </textbox>
</form>

# textbox.component.html
<div class="textbox-wrapper">
  <ng-content>
</div>

Restrictions are like following.

  • TextboxComponent has ng-content and need to project input element to it.
  • Emit an event in TextboxComponent when input element is inputted something.
  • Don’t wanna make input element to have more attributes, e.g. <input type="text" (input)="event()">.

I was writing code, but cannot find a solution…

# input.directive.ts
@Directive({ selector: 'input', ... })
export class InputDirective {
  ngOnChanges(): void {
    // ngOnChanges() can observe only properties defined from @Input Decorator...
  }
}

# textbox.component.ts
@Component({ selector: 'textbox', ... })
export class TextboxComponent {
  @ContentChildren(InputDirective) inputs: QueryList<InputDirective>;
  ngAfterContentInit(): void {
    this.inputs.changes.subscribe((): void => {
      // QueryList has a changes property, it can observe changes when the component add/remove.
      // But cannot observe input changes...
    });
  }
}

5
Leave a Reply

avatar
5 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
1 Comment authors
Jason Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Jason
Guest

The input event is bubbling and can be listened on the parent component

<div class="textbox-wrapper" (input)="inputChanged($event)">
  <ng-content></ng-content>
</div> 

Plunker example

Jason
Guest

In ngAfterViewInit(), find the element(s) of interest, then imperatively add event listener(s). The code below assumes only one input: @Component({ selector: 'textbox', template: `<h3>textbox value: {{inputValue}}</h3> <div class="textbox-wrapper"> <ng-content></ng-content> </div>`, }) export class TextboxComp { inputValue:string; removeListenerFunc: Function; constructor(private _elRef:ElementRef, private _renderer:Renderer) {} ngAfterContentInit() { let inputElement = this._elRef.nativeElement.querySelector('input'); this.removeListenerFunc = this._renderer.listen( inputElement, 'input', event => this.inputValue = event.target.value) } ngOnDestroy() { this.removeListenerFunc(); } } Plunker This answer is essentially an imperative approach, in contrast to G√ľnter’s declarative approach. This approach may be easier to extend if you have multiple inputs. There doesn’t seem to be a way to use… Read more »

Jason
Guest

You could use the following scenario, wich has hostbinding property on input directive input.directive.ts import {Directive, HostBinding} from 'angular2/core'; import {Observer} from 'rxjs/Observer'; import {Observable} from 'rxjs/Observable'; @Directive({ selector: 'input', host: {'(input)': 'onInput($event)'} }) export class InputDirective { inputChange$: Observable<string>; private _observer: Observer<any>; constructor() { this.inputChange$ = new Observable(observer => this._observer = observer); } onInput(event) { this._observer.next(event.target.value); } } And then your TextBoxComponent will subscribe on Observable object that defined above in InputDirective class. textbox.component.ts import {Component, ContentChildren,QueryList} from 'angular2/core'; import {InputDirective} from './input.directive'; @Component({ selector: 'textbox', template: ` <div class="textbox-wrapper"> <ng-content></ng-content> <div *ngFor="#change of changes"> {{change}} </div> </div> `… Read more »

Jason
Guest

You could add an ngControl on your input element.

<form>
  <textbox>
    <input ngControl="test"/>
  </textbox>
</form>

This way you would be able to use NgControl within ContentChild. It tiges access to the valueChanges property you can register on to be notified of updates.

@Component({ selector: 'textbox', ... })
export class TextboxComponent {
  @ContentChild(NgControl) input: NgControl;
  ngAfterContentInit(): void {
    this.input.control.valueChanges.subscribe((): void => {
      (...)
    });
  }
}

Jason
Guest

You can use Angular CDK observers https://material.angular.io/cdk/observers/api

import this module in your module

import { ObserversModule } from '@angular/cdk/observers';

then use in ng-content's parent element

<div class="projected-content-wrapper (cdkObserveContent)="contentChanged()">
 <ng-content></ng-content>
</div>