Дитина слухає батьківську подію в Angular 2


82

У кутових документах є тема про прослуховування дитячих подій від батьків. Це добре. Але моя мета - щось зворотне !. У моєму додатку є "admin.component", який містить вигляд макета сторінки адміністратора (меню бічної панелі, панель завдань, стан тощо). У цьому батьківському компоненті я налаштував систему маршрутизатора для зміни основного подання між іншими сторінками адміністратора. Проблема полягає в збереженні речей після змін, користувач натискає кнопку збереження на панелі завдань (яка розміщується в admin.component), а дочірній компонент повинен прослухати цю подію клацання для виконання персоналу збереження.


2
здається, найкращою практикою для цього є використання сервісу та спостережувана диспетчеризація події.
zpul

Ваше питання не надто відрізняється від цього: stackoverflow.com/questions/35560860 / ...
freethebees

@freethebees Можливо, рішення одне і те ж, але форма проблеми інша, і я маю намір знайти найкращий підхід до цієї ситуації.
Хамед Хамеді

Ми не збираємося встановлювати службу для однієї події, переданої дочірньому компоненту.
TR3B

Відповіді:


101

Я думаю, що цей документ може бути вам корисним:

Насправді ви могли б використовувати спостережуваний предмет, який батько надає своїм дітям. Щось таке:

@Component({
  (...)
  template: `
    <child [parentSubject]="parentSubject"></child>
  `,
  directives: [ ChildComponent ]
})
export class ParentComponent {
  parentSubject:Subject<any> = new Subject();

  notifyChildren() {
    this.parentSubject.next('some value');
  }
}

Дочірній компонент може просто підписатися на цю тему:

@Component({
  (...)
})
export class ChildComponent {
  @Input()
  parentSubject:Subject<any>;

  ngOnInit() {
    this.parentSubject.subscribe(event => {
      // called when the notifyChildren method is
      // called in the parent component
    });
  }

  ngOnDestroy() {
    // needed if child gets re-created (eg on some model changes)
    // note that subsequent subscriptions on the same subject will fail
    // so the parent has to re-create parentSubject on changes
    this.parentSubject.unsubscribe();
  }

}

В іншому випадку ви можете використати спільну послугу, що містить таку тему, подібним чином ...


Зворотний виклик у моєму дочірньому компоненті не запускатиметься, якщо батьківський компонент не транслює наступне значення всередині ngInit. Чому це так?
cjsimon

5
Чи є якісь переваги використання цього методу перед ViewChild?
sunnyiitkgp

Незважаючи на те, що це виглядає більш професійно, давши кілька годин на експерименти в моїй ситуації, я не зміг уникнути проблем із UnsubscribeErrors після додавання / видалення дочірніх елементів (і, таким чином, скасування підписки на тему). Тож за допомогою TimeBoxing я пішов на набагато простішу відповідь @StephenPaul. У будь-якому випадку, дякую за відповідь, і, мабуть, я просто зараз недостатньо добре розумію бібліотеку rxjs, щоб повністю її зрозуміти та внести правильні (додаткові) корективи у ваш приклад, щоб він працював бездоганно.
Бернуллі, IT

116

Заради нащадків я просто подумав, що я згадаю про більш традиційне рішення цього : просто отримайте посилання на ViewChild, а потім безпосередньо викликайте один із його методів.

@Component({
  selector: 'app-child'
})
export class ChildComponent {

  notifyMe() {
    console.log('Event Fired');
  }
}

@Component({
  selector: 'app-parent',
  template: `<app-child #child></app-child>`
})
export class ParentComponent {

  @ViewChild('child')
  private child: ChildComponent;

  ngOnInit() {
    this.child.notifyMe();
  }
}

3
Він падає , як тільки я входжу в батьківський компонент ... Cannot read property 'notifyMe' of undefined.
ласкавий користувач

1
Я пішов за цим дуже простим рішенням порівняно з якимось більш професійним рішенням для почуттів @ThierryTemplier (див. Мій коментар там).
Бернуллі, IT

2
@BernoulliIT ви робите хороший момент. Я припускаю, що наприкінці дня вони роблять приблизно те саме. Моє рішення використовує менше коду. Його рішення означає, що батьківський компонент не повинен вводити дочірній компонент для надсилання йому повідомлень. RXJS чудовий, але я часто бачу його надмірне використання в рамках Angular-проектів. Люди думають, що це «срібна куля» чи щось інше. Це насправді може ускладнити речі набагато складніше, ніж потрібно. Подивіться на його коментар всередині його ngOnDestroy()методу. Що стосується мене, це занадто неясно. Це просто помилка, яка чекає, що трапиться IMHO.
Stephen Paul

1
"Подивіться на його коментар усередині його методу ngOnDestroy ()." Я думаю, що це саме те, від чого я «страждав», спробувавши такий підхід, тоді я застряг і не мав стільки часу на подальші розслідування (для комерційного проекту, над яким я працюю). Хоча це "трохи зачепило" моє професійне серце;)
Бернуллі IT

1
Дякую @StephenPaul, для мене Батьківські дзвінки метод Viewchild був хорошим, але ваша відповідь допомогла мені досягти своєї долі!
MKJ

14

Тут я б міг підходити до більш оголених кісток, якщо я правильно зрозумію питання. Припущення -

  • OP має кнопку збереження в батьківському компоненті
  • Дані, які потрібно зберегти, містяться у дочірніх компонентах
  • До всіх інших даних, які можуть знадобитися дочірньому компоненту, можна отримати доступ із сервісів

У батьківському компоненті

<button type="button" (click)="prop1=!prop1">Save Button</button>
<app-child-component [setProp]='prop1'></app-child-component>

І в дитині ..

prop1:boolean;
  @Input()
  set setProp(p: boolean) {
    // -- perform save function here
}

Це просто посилає клацання кнопки дочірньому компоненту. Звідти дочірній компонент може зберігати дані самостійно.

РЕДАКТУВАТИ: якщо дані з батьківського шаблону також потрібно передавати разом із натисканням кнопки, це також можливо при такому підході. Повідомте мене, якщо це так, і я оновлю зразки коду.


0

Для тих, хто отримує Cannot read property 'notifyMe' of undefined

Спробуйте викликати метод всередині ngAfterViewInit()intead зngOnInit()

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.