2016-04-23 3 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 имеет ng-content и нужно проецировать input элемент к нему.
  • Извлечь событие в текстовое полеКомпонент, когда элемент 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', и это многословно, если я хочу использовать его. Но я могу использовать 'host: {(input): 'event()'}' свойство в InputDirective. Самая большая проблема заключается в том, что TextboxComponent с использованием 'ng-content' может обнаруживать, когда InputDirective вводится что-либо. –

ответ

5

input события барботирования и можно слушать в родительском компоненте

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

Plunker example

+0

Oh! Я не знал этого ... Я мог бы ожидать, что поведение будет! Спасибо!! –

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> 

Таким образом, вы сможете использовать 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

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

Plunker

Этот ответ является по существу императив подход, в отличие от декларативного подхода Gunter в. Этот подход может быть проще расширить, если у вас есть несколько входов.


Там, кажется, не быть способ использовать @ContentChild() (или @ContentChildren()), чтобы найти DOM элементов в поставляемого пользователем шаблона (то есть, нг-контенте) ... что-то вроде @ContentChild(input) Безразлично» t, похоже, существует. Поэтому я использую querySelector().

В этом сообщении, http://angularjs.blogspot.co.at/2016/04/5-rookie-mistakes-to-avoid-with-angular.html, Kara предлагает определить директиву (например, InputItem) с помощью селектора input, а затем с помощью @ContentChildren(InputItem) inputs: QueryList<InputItem>;. Тогда нам не нужно использовать querySelector(). Однако мне не особенно нравится этот подход, потому что пользователь TextboxComponent должен каким-то образом знать, что он также включает InputItem в массив directives (думаю, какая-то документация по компонентам может решить проблему, но я все еще не поклонник). Для этого подхода используется 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> 
Смежные вопросы