2016-04-23 12 views
6

子要素の入力が変化したときに親要素の関数を呼び出す方法は?入力要素の変化を観測する方法

以下はHTML構造です。

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

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

制限事項は次のとおりです。

  • TextboxComponentはng-contentを持っており、それにinput要素を投影する必要があります。
  • input要素が入力されたときに、TextboxComponentでイベントを送出します。
  • input要素にさらに多くの属性を追加したくないとします。 <input type="text" (input)="event()">

私は、コードを書いていたが、解決策を見つけることができません...

# 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

は、なぜあなたは、出力を使用したくないあなたのモジュールの角CDKのオブザーバーhttps://material.angular.io/cdk/observers/api

インポートこのモジュールを使用することができますバインディングは、のように?他の開発者(またはあなたも)がTextboxComponentのマークアップに入力要素だけを追加することをどうやって確認できますか? – rrjohnson85

+0

出力バインディングを 'input'要素に追加する必要があるので、それを使用したい場合は冗長です。しかし、代わりに 'host:{(input): 'event()'}'プロパティをInputDirectiveで使うことができます。 最大の問題は、 'ng-content'を使っているTextboxComponentがInputDirectiveが何かを入力した時を検出できることです。 –

答えて

5

inputイベントがバブリングされ、関心のある要素(単数または複数)を見つけ、親コンポーネントngAfterViewInit()

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

Plunker example

+0

ああ!私はそれを知らなかった...私は期待される行動を得ることができた!ありがとうございました!! –

1

あなたはウィッヒが入力ディレクティブに

input.directive.tsをプロパティをhostbindingた、次のシナリオを使用することができます

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

そしてあなたTextBoxComponentが観察可能にサブスクライブします上で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)); 
    }); 
    } 
} 

ここでは、あなたの入力要素にngControlを追加することができplunker sample

+0

ありがとうございます。最初はRxJSを使う必要があると思いますが、それはとても複雑です。 –

0

です。

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

このようにして、NgControlをContentChildで使用できるようになります。これは、更新の通知を受けるために登録できるvalueChangesプロパティへのアクセスを提供します。

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

ありがとうございます。 'ngAfterContentInit'を使用したために期待できません。 'ngAfterViewChecked'を使用するとイベントが発生しましたが、何度も実行されました... –

4

に聴くことができ、その後、命令的(イベントリスナーを追加s)。

@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

この答えはギュンターの宣言的なアプローチとは対照的に、基本的に不可欠なアプローチである:以下のコードは、一つだけの入力を前提としています。このアプローチは、複数の入力がある場合には拡張が容易です。


「doesnの@ContentChild(input)ような何か...ユーザー提供のテンプレート(すなわち、NG-コンテンツ内容)でDOM要素を見つけるために@ContentChild()(または@ContentChildren())を使用する方法はないようです存在しているように見える。したがって私がquerySelector()を使用する理由。このブログ記事で

http://angularjs.blogspot.co.at/2016/04/5-rookie-mistakes-to-avoid-with-angular.html、カラはinputセレクターで(InputItemを言う)指令を定義して、@ContentChildren(InputItem) inputs: QueryList<InputItem>;を使用することを提案しています。その後、querySelector()を使用する必要はありません。 しかし、TextboxComponentのユーザーは、directives配列にInputItemを含めることを何とか知っていなければならないので、私は特にこの方法が嫌いです(いくつかのコンポーネント文書が問題を解決できると思いますが、まだファンではありません)。この方法にはplunkerがあります。

+0

ああ、ありがとう!私は参考にこれを使用します。 –

0

その後、ng-content's親要素に使用

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

あなたは

<div class="projected-content-wrapper (cdkObserveContent)="contentChanged()"> 
<ng-content></ng-content> 
</div> 
関連する問題