Наблюдатели не удаляются из Субъекта, когда директива уничтожена в Angular 4
У меня возникают проблемы при отписке от темы, когда директива, в которой установлена подписка, уничтожена. Рассмотрим следующий HTML:
<ng-container *ngFor="let item of items; let id = index">
<div [toggleCollapsible]="'target'+id">
{{ item.label }}
</div>
<div *toggleCollapsibleTarget="'target'+id">
<h1>Some nice content up in here</h1>
</div>
</ng-container>
toggleCollapsible
Директива получает и @Input()
с уникальным идентификатором, который будет использоваться для определения того, какой контент должен свернуться / раскрыться, что делается *toggleCollapsibleContent
структурная директива. Связь между этими двумя директивами осуществляется с помощью службы под названием toggleCollapsibleService
,
Вот код для toggleCollapsible
директивы. Я опускаю некоторые вещи для удобства чтения:
@Directive({
selector: "[toggleCollapsible]",
host: {
"(click)": "_onClick($event)",
}
})
export class toggleCollapsibleDirective {
@Input('toggleCollapsible') target: string;
isOpen: boolean;
constructor(private _toggle: toggleCollapsibleService) {}
_onClick(e) {
this._toggle.toggleContent(this.target, this.isOpen);
this.isOpen = !this.isOpen;
}
}
Обычно при щелчке по элементу хоста вызывается сервисный метод, который получает 2 параметра: имя цели и разборщик в данный момент открыты или нет. Теперь мой toggleCollapsibleService
:
@Injectable()
export class toggleCollapsibleService {
targetName: string;
private togglerState$: Subject<boolean> = new Subject();
toggleContent(target: string, currentState: boolean) {
this.targetName = target;
this.togglerState$.next(!currentState);
}
}
Таким образом, в основном это просто сохранение идентификатора складного объекта, который будет открыт / закрыт, и передачи соответствующего значения (опять же, должен ли он открываться или закрываться). Посмотрим *toggleCollapsibleContent
вот где все становится сложнее:
@Directive({
selector: "[toggleCollapsibleContent]"
})
export class toggleCollapsibleContentDirective {
private _name: string;
@Input("toggleCollapsibleContent")
set name(name: string) {
this._name = name;
this._toggle.togglerState$.subscribe(status => {
if (this._name == this._toggle.targetName && status) {
this.renderTarget();
} else if (this._name == this._toggle.targetName) {
this.unmountTarget();
}
});
}
constructor(
private _view: ViewContainerRef,
private _template: TemplateRef<any>,
private _toggle: toggleCollapsibleService
) {}
renderTarget() {
this._view.createEmbeddedView(this._template);
}
unmountTarget() {
if (this._view) this._view.clear();
}
}
Структурная директива работает нормально, поэтому с этой стороной реализации проблем нет. Итак, проблема в том, скажем, у меня есть фрагмент HTML на моем HomeComponent
и items
коллекция имеет длину 2. Это означает, что я создаю 2 экземпляра *toggleCollapsibleContent
структурная директива, каждый подписывается на togglerState$
Предмет. Если проверить через console.log
togglerState$
объект я получаю, что мой объект имеет 2 наблюдателя, что является ожидаемым поведением, по одному для каждого экземпляра *toggleCollapsibleContent
,
Однако, если я перейду к другому маршруту и выполню рендеринг другого компонента и т. Д., togglerState$
Тема все еще существует, и когда я возвращаюсь к своему /home
маршрут, где HomeComponent
загружен, togglerState $ добавляет еще 2 наблюдателя, и так как оригинальные все еще там, теперь у меня есть 4 наблюдателя, 2 для каждого экземпляра *toggleCollapsibleContent
директива и, следовательно, мой контент дублируется.
У кого-нибудь есть идеи, почему это происходит?
1 ответ
Вам необходимо отписаться явно:
this.subscription = this._toggle.togglerState$.subscribe(status => { ...
...
ngOnDestroy() {
this.subscription.unsubscribe();
}