Наблюдатели не удаляются из Субъекта, когда директива уничтожена в 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();
}
Другие вопросы по тегам