Как передать событие от родителя к ребенку?

112

В настоящее время я использую Angular 2. Обычно мы используем @Output() addTab = new EventEmitter<any>();а затем addTab.emit()для отправки события родительскому компоненту.

Есть ли способ сделать это по-другому, от родителя к ребенку?

code1
источник
2
Просто измените значение выражения, переданного в качестве входных данных дочернему компоненту, и дочерний компонент получит его (и получит информацию от ngOnChanges). Вы также можете создать событие, используя общую службу, у которой есть Observable.
JB Nizet

Ответы:

193

Используя RxJs , вы можете объявить a Subjectв родительском компоненте и передать его как Observableдочернему компоненту, дочернему компоненту просто нужно подписаться на это Observable.

Родительский компонент

eventsSubject: Subject<void> = new Subject<void>();

emitEventToChild() {
  this.eventsSubject.next();
}

Родительский HTML

<child [events]="eventsSubject.asObservable()"> </child>    

Дочерний компонент

private eventsSubscription: Subscription;

@Input() events: Observable<void>;

ngOnInit(){
  this.eventsSubscription = this.events.subscribe(() => doSomething());
}

ngOnDestroy() {
  this.eventsSubscription.unsubscribe();
}
BlueNC
источник
13
Кроме того, с помощью этого подхода данные могут передаваться вместе с событием. Например, this.eventsSubject.next({data});в родительском, потом this.events.subscribe(({data}) => doSomething(data));в дочернем.
Винс
2
Я отредактировал этот отличный ответ, чтобы добавить отказ от подписки. +1
Умар Фарук Хаваджа
1
Вероятно, здесь возникает вопрос новичка: зачем конвертировать eventsSubjectв Observable, а не просто подписываться на него как на Subject?
Джастин Морган
11
Преобразование eventsSubject в Observable предотвращает вызов дочернего компонента next ().
BlueNC 02
2
Спасибо за это решение, ЭТО РАБОТАЕТ, но в консоли появляется сообщение об ошибке: «core.js: 6185 ERROR TypeError: Cannot read property 'subscribe' of undefined» Ошибка указывает на events.subscribe () в ngOnInit дочернего компонента: "this.eventsSubscription = this.events.subscribe (() => doSomething ());" Я использую версии: "@ angular / cli": "~ 9.1.0" и "rxjs" : "~ 6.5.4"
Эдуардо
72

Насколько мне известно, это можно сделать двумя стандартными способами.

1. @ Вход

Каждый раз, когда данные в родительском элементе изменяются, дочерний элемент получает уведомление об этом в методе ngOnChanges. Ребенок может действовать по нему. Это стандартный способ взаимодействия с ребенком.

Parent-Component
public inputToChild: Object;

Parent-HTML
<child [data]="inputToChild"> </child>       

Child-Component: @Input() data;

ngOnChanges(changes: { [property: string]: SimpleChange }){
   // Extract changes to the input property by its name
   let change: SimpleChange = changes['data']; 
// Whenever the data in the parent changes, this method gets triggered. You 
// can act on the changes here. You will have both the previous value and the 
// current value here.
}
  1. Общая концепция обслуживания

Создание службы и использование наблюдаемого в общей службе. Ребенок подписывается на него, и всякий раз, когда происходит изменение, ребенок будет уведомлен. Это тоже популярный метод. Если вы хотите отправить что-то, кроме данных, которые вы передаете в качестве входных, это можно использовать.

SharedService
subject: Subject<Object>;

Parent-Component
constructor(sharedService: SharedService)
this.sharedService.subject.next(data);

Child-Component
constructor(sharedService: SharedService)
this.sharedService.subject.subscribe((data)=>{

// Whenever the parent emits using the next method, you can receive the data 
in here and act on it.})
Prajwal Gonsalves
источник
Я попробовал первый метод, и до некоторого момента он работал нормально, а после этого я получил сообщение об ошибке, в котором говорилось, что значение было изменено, раньше оно было ложным, а теперь истинным. Никаких дополнительных объяснений, кроме этого, мне не дало.
code1,
2
Если вы имеете в виду ExpressionChangedAfterItHasBeenCheckedError, эта ошибка не будет выдана в рабочем режиме.
user1337
<child [data]="inputToChild"> </child>вероятно, должны быть <child [data]="(inputToChild)"> </child>изменения
pmc
28

В родительском компоненте вы можете использовать @ViewChild () для доступа к методу / переменной дочернего компонента.

@Component({
  selector: 'app-number-parent',
  templateUrl: './number-parent.component.html'
})
export class NumberParentComponent {
    @ViewChild(NumberComponent)
    private numberComponent: NumberComponent;
    increase() {
       this.numberComponent.increaseByOne();
    }
    decrease() {
       this.numberComponent.decreaseByOne();
    }
} 

Обновить:

Угловой 8 и далее -

@ViewChild(NumberComponent, { static: false })
Хардик Патель
источник
4
Это кажется более чистым, чем работа с наблюдаемыми. Недостаток в том, что нужно иметь ввиду ребенка. Если дочерний элемент загружен, например, маршрутизацией, это не удастся, как numberComponentи будет undefined.
Shy Agam
1
это хорошая практика? Я согласен с тем фактом, что это чище, чем наблюдаемые, но я сомневаюсь, что можно управлять дочерними переменными от родителя. В любом случае, это сработало так хорошо для моих нужд, спасибо!
Takatalvi
1
Это хороший совет. Похоже, что @ViewChild изменен в Angular 8, или, по крайней мере, мне пришлось указать параметры ViewChild. Я использовал это в своем случае: @ViewChild (Other, {static: false}) private otherComponent: Other;
Tore Aurstad
8

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

В дочернем компоненте вы объявляете его как есть:

@Input() myInputName: myType

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

Пример :

<my-child-component [myChildInputName]="myParentVar"></my-child-component>

Но будьте осторожны, объекты передаются как ссылка, поэтому, если объект обновляется в дочернем элементе, родительский var будет слишком обновлен. Иногда это может привести к нежелательному поведению. С первичными типами значение копируется.

Чтобы пойти дальше, прочтите это:

Документы: https://angular.io/docs/ts/latest/cookbook/component-communication.html

Ноки
источник
1

В родительском элементе вы можете ссылаться на дочерний элемент с помощью @ViewChild. При необходимости (т. Е. Когда событие будет запущено) вы можете просто выполнить метод в дочернем элементе от родителя, используя ссылку @ViewChild.

Пол Эванс
источник