How to observe input element changes in ng-content

后端 未结 5 1599
[愿得一人]
[愿得一人] 2021-01-17 18:03

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

The below is HTML structure.

# app.comopnent.html
相关标签:
5条回答
  • 2021-01-17 18:26

    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>
    
    0 讨论(0)
  • 2021-01-17 18:32

    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>
      `
    })
    export class TextboxComponent {
      private changes: Array<string> = [];
      @ContentChildren(InputDirective) inputs: QueryList<InputDirective>;
    
      onChange(value, index) {
        this.changes.push(`input${index}: ${value}`);
      }
    
      ngAfterContentInit(): void {
        this.inputs.toArray().forEach((input, index) => {
          input.inputChange$.subscribe(value => this.onChange(value, index + 1));
        });
      }
    }
    

    Here's plunker sample

    0 讨论(0)
  • 2021-01-17 18:40

    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 @ContentChild() (or @ContentChildren()) to find DOM elements in the user-supplied template (i.e, the ng-content content)... something like @ContentChild(input) doesn't seem to exist. Hence the reason I use querySelector().

    In this blog post, http://angularjs.blogspot.co.at/2016/04/5-rookie-mistakes-to-avoid-with-angular.html, Kara suggests defining a Directive (say InputItem) with an input selector and then using @ContentChildren(InputItem) inputs: QueryList<InputItem>;. Then we don't need to use querySelector(). However, I don't particularly like this approach because the user of the TextboxComponent has to somehow know to also include InputItem in the directives array (I guess some component documentation could solve the problem, but I'm still not a fan). Here's a plunker for this approach.

    0 讨论(0)
  • 2021-01-17 18:46

    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 => {
          (...)
        });
      }
    }
    
    0 讨论(0)
  • 2021-01-17 18:50

    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

    0 讨论(0)
提交回复
热议问题