2016-04-23 11 views
6

Come chiamare la funzione del componente genitore quando il componente figlio ha osservato le modifiche di input?Come osservare le modifiche degli elementi di input in ng-content

Quanto segue è una struttura HTML.

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

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

Le restrizioni sono come segue.

  • TextboxComponent ha ng-content e deve proiettare l'elemento input.
  • Emettere un evento in TextboxComponent quando l'elemento input immette qualcosa.
  • Non voglio rendere l'elemento input più attributi, ad es. <input type="text" (input)="event()">.

stavo scrivendo il codice, ma non riesco a trovare una soluzione ...

# 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... 
    }); 
    } 
} 
+0

Perché non si desidera utilizzare un'uscita vincolante, come ? Come puoi essere sicuro che altri sviluppatori (o anche tu) aggiungano solo elementi di input al markup di TextboxComponent? – rrjohnson85

+0

Perché dovrei aggiungere un'associazione di output all'elemento 'input' ed è prolissa se voglio usarla. Ma posso usare 'host: {(input): 'event()'}' proprietà in InputDirective. Il problema più grande è che TextboxComponent che usa 'ng-content' può rilevare quando InputDirective è stato immesso in input. –

risposta

5

L'evento input sta bollendo e può essere ascoltata sul componente genitore

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

Plunker example

+0

Oh! Non lo sapevo ... avrei potuto ottenere un comportamento previsto !! Grazie!! –

1

È possibile utilizzare il seguente scenario, che vanta hostbinding proprietà su direttiva di ingresso

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); 
    } 
} 

E poi il tuo TextBoxComponent sottoscriverà il osservabile oggetto definito sopra nella classe InputDirective.

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)); 
    }); 
    } 
} 

Ecco plunker sample

+0

Grazie. All'inizio, penso di aver bisogno di usare RxJS ma è così complicato ... –

0

Si potrebbe aggiungere un ngControl sul vostro elemento di input.

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

In questo modo è possibile utilizzare NgControl in ContentChild. Tiene l'accesso alla proprietà valueChanges su cui puoi registrarti per ricevere notifiche sugli aggiornamenti.

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

Grazie. Non posso aspettarmi a causa dell'uso di 'ngAfterContentInit'. Ho ricevuto un evento durante l'uso di 'ngAfterViewChecked', ma è stato eseguito molte volte ... –

4

In ngAfterViewInit(), trovare l'elemento (s) di interesse, quindi imperativamente aggiungere listener di eventi (S). Il seguente codice assume solo ingresso:

@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

Questa risposta è essenzialmente un approccio imperativo, in contrasto con l'approccio dichiarativa di Günter. Questo approccio potrebbe essere più semplice da estendere se si hanno più input.


Non sembra essere un modo per utilizzare @ContentChild() (o @ContentChildren()) per trovare elementi DOM nel modello fornito dall'utente (ad esempio, il contenuto ng-contenuto) ... qualcosa come @ContentChild(input) doesn' sembra esistere Da qui il motivo per cui utilizzo querySelector().

In questo post del blog, http://angularjs.blogspot.co.at/2016/04/5-rookie-mistakes-to-avoid-with-angular.html, Kara suggerisce di definire una direttiva (ad esempio InputItem) con un selettore input e quindi utilizzando @ContentChildren(InputItem) inputs: QueryList<InputItem>;. Quindi non è necessario utilizzare querySelector(). Tuttavia, non mi piace particolarmente questo approccio perché l'utente di TextboxComponent deve in qualche modo sapere di includere anche InputItem nell'array directives (suppongo che alcuni componenti della documentazione possano risolvere il problema, ma non sono ancora un fan). Ecco un plunker per questo approccio.

+0

oh, grazie! Userò questo per riferimento. –

0

È possibile utilizzare osservatori CDK angolari https://material.angular.io/cdk/observers/api

importare questo modulo nel modulo

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

quindi utilizzare in elemento ng-content's genitore

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