Angular 2 - Двусторонняя связь между компонентами

У меня есть этот код... Это пример учебного приложения, которое я пытаюсь создать, отражающее ежедневные потребности разработчика. Фактически, когда пользователь набирает "огонь" на родительском компоненте, дочерний элемент выполняет событие, которое отправляет родительскому слову "booom". Это образец для демонстрации связи между дочерним компонентом, отправляющим сообщения, в родительский компонент с использованием @Input и OnChanges.

Теперь я пытаюсь сделать другое: родитель должен каким-то образом сообщить ребенку сообщение, подобное "Target Locked", когда пользователь нажимает клавишу ввода (keyCode = 13). При этом у нас будет сценарий двухсторонней связи между компонентами.

Каков наилучший подход?

child.component

import {Component, Input, OnChanges, EventEmitter,Output, Injectable} from 'angular2/core';
@Injectable()
@Component({
selector: 'child-component',
template: `<p>I'm the child component</p>
`
})
export class ChildComponent implements OnChanges { 
@Input() txt: string;
@Output() aim: EventEmitter<any> = new EventEmitter();
ngOnChanges(changes: {[propName: string]: SimpleChange}) {
    var t = changes['txt'].currentValue;
    if(t == 'fire') {
        console.log('Fire !!!');
        this.aim.emit("booom !!!");
    }
}
}

parent.component

import {Component} from 'angular2/core';
import {ChildComponent} from './child.component'
@Component({
selector: 'parent-component',
directives : [ChildComponent]
template: `<p>I'm the parent component</p>
<input type="textbox" [(ngModel)]="theModel" (keydown)="arrow($event)">
<p>feedback: {{feedback}}</p>
<child-component txt="{{theModel}}" (aim)="feedback=$event"></child-component>
`
})
export class ParentComponent { 
theModel;
feedback;
arrow (evt){
    if(evt.keyCode ==13) {
        //Need to cause an event on the child - a message like "Target Locked"
    };
}
}

Ответ 1

Мое сомнение в том, чтобы сделать обратный путь: ребенок захватывает событие родителя. Помните, что у ребенка никогда не будет селектора родителя. Вот почему это действительно отличается.

Я думаю, что замешательство связано с тем, что вам не нужно событие. Для родителей → дочерняя связь, просто добавьте еще одно свойство ввода для ребенка. И привяжите к нему родительское свойство:

<child-component [anotherInputProperty]="someParentProperty" ...

Затем, когда вы изменяете значение someParentProperty в родительском компоненте, обнаружение изменения Angular будет распространять новое значение на дочерний:

if(evt.keyCode === 13) {
    // Need to cause an event on the child - a message like "Target Locked".
    // Just change the property value:
    this.someParentProperty = "some new value";
    // Angular will take care of propagating the new value to the child
};

Если вы хотите, чтобы дочерний элемент выполнял некоторую логику при изменении значения свойства ввода, реализуйте ngOnChanges() в дочернем элементе.

Если проблема в том, что вы не хотите каждый раз менять сообщение, вы можете либо

  • используйте совместно используемую службу с наблюдаемым и добавьте дочерний элемент subscribe() к наблюдаемому или
  • префикс или постфиксное сообщение со случайным значением и отделите его от сообщения с помощью | или какого-либо другого символа, который вы можете разбить, так что в случае ребенка вы можете легко извлечь сообщение.

Вы также можете использовать тему, а не наблюдаемую в общей службе: см. Родитель и дети обмениваются данными через службу.

Ответ 2

В качестве входного сигнала дочернего компонента можно указать EventEmitter:

@Component({
  selector: 'child-component'
  (...)
})
export class ChildComponent {
  @Input()
  parentEventEmitter:EventEmitter;

  ngOnInit() {
    this.parentEventEmitter.subscribe((event) => {

    });
  }
}

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

Этот EventEmitter будет предоставлен таким образом в родительском компоненте:

<child-component
   [parentEventEmitter]="theEventEmitterFromTheParent">
</child-component>

Ответ 3

Вам необходимо реализовать событие-эмиттер и подписаться на него в родительском компоненте. Имя вашего эмиттера должно соответствовать имени связанного значения + "Изменить". Пример: если ваше имя значения "sum", ваше событие должно быть "sumChange", так что вы можете сделать 2-стороннее связывание с родительским liike [(sum)] = "value". Вот пример plunk:

https://plnkr.co/edit/efOGIJ0POh1XQeRZctSx?p=preview