2016-04-23 67 views
6

如何在子組件的觀察輸入更改時調用父組件的函數?如何觀察ng內容中的輸入元素更改

以下是HTML結構。

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

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

限制如下。

  • TextboxComponent has ng-content and need to project input element to it。
  • 當輸入input元素時,在文本框組件中發出事件。
  • 不想讓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

爲什麼你不希望使用的輸出綁定,如?你怎麼能確定其他開發者(甚至你)只將輸入元素添加到TextboxComponent的標記中? – rrjohnson85

+0

因爲我應該在'input'元素上附加一個輸出綁定,如果我想使用它,它會很冗長。但是我可以在InputDirective中使用'host:{(input):'event()'}'屬性。 最大的問題是使用'ng-content'的TextboxComponent可以檢測InputDirective什麼時候輸入了什麼。 –

回答

5

input事件被鼓泡,並且可以在父組件

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

Plunker example

+0

哦!我不知道它......我可以得到預期的行爲!謝謝!! –

1

您可以使用下面的場景,已經至極上hostbinding輸入指令

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

然後你的TextBoxComponent將在Observable上訂閱在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)); 
    }); 
    } 
} 

這裏的plunker sample

+0

謝謝。起初,我認爲我需要使用RxJS,但它太複雜了...... –

0

你可以輸入你的元素上添加一個ngControl。

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

這樣你就可以在ContentChild中使用NgControl。它可以訪問您可以註冊的valueChanges屬性以通知更新。

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

謝謝。由於使用'ngAfterContentInit',我無法預料到。我使用'ngAfterViewChecked'時發出事件,但它執行了很多次... –

4

ngAfterViewInit()上被收聽,找到感興趣的元件(一個或多個),則強制性地添加事件監聽器( 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

這個答案基本上是一個勢在必行的做法,而相比之下,君特的聲明方式。如果您有多個輸入,則此方法可能更容易擴展。


似乎沒有被使用@ContentChild()(或@ContentChildren())找到用戶提供的模板(即NG-內容的內容)DOM元素的方式...像@ContentChild(input)沒有按」似乎存在。因此我使用querySelector()的原因。

在這篇博文中,http://angularjs.blogspot.co.at/2016/04/5-rookie-mistakes-to-avoid-with-angular.html,Kara建議用input選擇器然後使用@ContentChildren(InputItem) inputs: QueryList<InputItem>;來定義一個指令(比如InputItem)。那麼我們不需要使用querySelector()。 但是,我不特別喜歡這種方法,因爲TextboxComponent的用戶必須知道還包含directives數組中的InputItem(我猜一些組件文檔可以解決問題,但我仍然不是粉絲)。這是一個plunker這種方法。

+0

哦,謝謝!我將用它作爲參考。 –

0

您可以使用角CDK觀察家https://material.angular.io/cdk/observers/api

進口該模塊的模塊中

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

然後在ng-content's父元素使用

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