Как узнать, является ли компонент в настоящее время загруженным компонентом в router-outlet

У меня есть приложение с лениво загруженными модулями.

В одном конкретном модуле с ленивой загрузкой у меня есть родительский компонент, который имеет router-outlet.

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

каждый дочерний компонент (который загружается в router-outlet) подписывается на это событие, используя один и тот же общий сервис!

Я попытался воссоздать свой вариант использования в следующем plunkr, используя демонстрацию героев.

Откройте Инструменты разработчика, чтобы просмотреть console сообщений.

  • Нажмите на ссылку Heroes (она должна быть выбрана по умолчанию).
  • Нажмите на элемент героя из списка.
  • Нажмите на ссылку Admin.
  • Нажмите кнопку Emit Test (верхняя правая навигация)

В консоли вы увидите, что событие было получено обоими компонентами, даже если оба компонента не должны быть «активными» !!!

1) Это задумано?

2) Как узнать, является ли текущий Компонент активным?


РЕЗЮМЕ/ОБНОВЛЕНИЕ

Насколько я понимаю, на EventEmitter/Subject можно подписаться сколько угодно раз, но при отказе от подписки вы не можете использовать его/подписаться на него AFAIK.

Мое решение было:

1) в сервисе я закончил делать следующее:

get(){
  if (!this.testEvent.closed) {
      this.testEvent.complete();
      this.testEvent.unsubscribe();
    }
    this.testEvent= new EventEmitter<void>();
    return this.testEvent;
  }
}

2) В каждом компоненте подписываюсь на событие, добавил ngDestroy и отписал событие:

  ngOnDestroy() {
    this.testEvent.unsubscribe();
  }

person A.Akram    schedule 06.11.2016    source источник
comment
Разместите соответствующий код здесь. В вашем plunkr много много файлов, и большинство из них совершенно неактуальны, и вы не сказали, какие файлы смотреть.   -  person JB Nizet    schedule 06.11.2016
comment
app/heroes/hero-list.component.ts и app/heroes/hero-detail.component.ts.   -  person A.Akram    schedule 06.11.2016
comment
Какое поведение вы хотели бы, чтобы приложение имело?   -  person Picci    schedule 06.11.2016
comment
@Picci, я думаю, когда я делаю event.emit(), он должен достигать только активных компонентов, которые загружены в DOM!   -  person A.Akram    schedule 06.11.2016
comment
@ А.Акрам, твоя догадка неверна. RxJS ничего не знает об angular, компонентах и ​​DOM. И даже если бы это было так, наблюдаемое даже не знает, что наблюдатель добавлен из компонента и не имеет прямой ссылки на компонент.   -  person JB Nizet    schedule 06.11.2016
comment
Я предполагаю, что когда дочерний элемент удаляется из router-outlet (DOM), экземпляр компонента больше не должен существовать!   -  person A.Akram    schedule 06.11.2016
comment
Вы вызываете unsubscribe() в testEvent в сервисе. Это не имеет смысла. Функция unsubscribe() должна вызываться из компонента в файле Subscription. Прочитайте два полученных вами ответа.   -  person JB Nizet    schedule 06.11.2016


Ответы (2)


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

У вас есть две составляющие. Это не синглтоны. Они создаются, когда должны отображаться на странице, и уничтожаются, когда их больше не должно быть.

И когда компоненты созданы, вы подписываетесь на наблюдаемое внутри службы singleton. Что это означает? Это означает, что наблюдаемый должен хранить ссылку на вашего наблюдателя, который сам имеет неявную ссылку (это) на экземпляр компонента. Без этой ссылки наблюдаемый объект не сможет вызвать наблюдателя при возникновении события. Таким образом, несмотря на то, что Angular больше не нуждается в вашем экземпляре компонента, наблюдаемый по-прежнему имеет ссылку на него и вызывает его, когда генерируется событие.

Таким образом, вам нужно отказаться от подписки, когда компонент больше не нужен:

private subscription: Subscription;

ngOnInit() {
    this.subscription = this.service.getTestEvent().subscribe(...);
}

ngOnDestroy() {
    this.subscription.unsubscribe();
}

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

person JB Nizet    schedule 06.11.2016
comment
Спасибо, понятно... Попробую - person A.Akram; 06.11.2016
comment
Теперь, после unsubscribe(), когда я пытаюсь подписаться в другом компоненте, я получаю сообщение об ошибке ObjectUnsubscribedError: object unsubscribed - person A.Akram; 06.11.2016

Дело в том, что вы никогда не отписываетесь от события. Чем больше вы нажимаете на своих пользователей, тем больше строк вы увидите в консоли. Каждый раз, когда вы нажимаете, вы подписываетесь на событие, то есть вы добавляете обратный вызов, который будет выполняться каждый раз, когда возникает событие, если вы не отмените подписку. В методе ngOnDestroy() вы должны вызвать метод unsubscribe() для подписки (это объект, возвращаемый методом subscribe()).

Другими словами, что-то вроде этого

subscrition: Subscription;

ngOnInit() {

    this.subscription = this.service.getTestEvent().subscribe( (e) => {
      console.log('HeroListComponent ==>');
    });

.....

}

ngOnDestroy() {
    this.subscription.unsubscribe();
}

Надеюсь, это поможет.

person Picci    schedule 06.11.2016
comment
Теперь, после unsubscribe(), когда я пытаюсь подписаться в другом компоненте, я получаю сообщение об ошибке ObjectUnsubscribedError: object unsubscribed - person A.Akram; 06.11.2016
comment
Трудно что-то сказать без простого примера и воспроизводимого сценария. Но вы не должны использовать EventEmitter в сервисе. Используйте тему RxJS. - person JB Nizet; 06.11.2016
comment
Используя тему, я получаю ту же ошибку. Единственное решение, которое я вижу, это каждый раз возвращать новый Subject/EventEmitter! Я имею в виду, что я не могу использовать тот же Subject/EventEmitter после отказа от подписки! это имеет смысл, но я не уверен! - person A.Akram; 06.11.2016