当子组件观察到输入发生变化时,如何调用父组件的功能?
以下是HTML结构。
# app.comopnent.html
<form>
<textbox>
<input type="text">
</textbox>
</form>
# textbox.component.html
<div class="textbox-wrapper">
<ng-content>
</div>
限制如下。
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 :(得分:9)
input
事件正在冒泡,可以在父组件上进行侦听
<div class="textbox-wrapper" (input)="inputChanged($event)">
<ng-content></ng-content>
</div>
答案 1 :(得分:5)
在ngAfterViewInit()
中,找到感兴趣的元素,然后命令性地添加事件监听器。下面的代码只假设一个输入:
@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();
}
}
与Günter的声明式方法相比,这个答案本质上是一种必要的方法。如果您有多个输入,这种方法可能更容易扩展。
似乎没有办法使用@ContentChild()
(或@ContentChildren()
)在用户提供的模板中查找DOM元素(即ng-content内容)......像@ContentChild(input)
似乎不存在。因此我使用querySelector()
的原因。
在这篇博客文章http://angularjs.blogspot.co.at/2016/04/5-rookie-mistakes-to-avoid-with-angular.html中,Kara建议使用input
选择器定义指令(比如InputItem),然后使用@ContentChildren(InputItem) inputs: QueryList<InputItem>;
。然后我们不需要使用querySelector()
。
但是,我并不特别喜欢这种方法,因为TextboxComponent的用户必须知道在directives
数组中也包含InputItem(我想一些组件文档可以解决问题,但我仍然不是风扇)。这是这种方法的plunker。
答案 2 :(得分: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将订阅上面在InputDirective类中定义的Observable对象。
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
答案 3 :(得分: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 => {
(...)
});
}
}
答案 4 :(得分:0)
您可以使用Angular 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>