在Angular 4

时间:2017-07-20 11:27:55

标签: angular rxjs subject-observer

当设置订阅的指令被销毁时,我在取消订阅主题时遇到问题。请考虑以下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指令接收带有唯一ID的@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);
    }
}

所以,基本上这只是保存可折叠的ID,它将打开/关闭并传递相应的值(再次,它应该打开或关闭)。让我们看看*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();
    }
}

结构指令工作正常,因此实现的那一方没有问题。问题是,假设我的HomeComponent上有HTML代码段,而items集合的长度为2.这意味着我创建了*toggleCollapsibleContent结构的2个实例指令,每个订阅togglerState$主题。如果通过console.log检查togglerState$对象,我得到的是我的对象有2个观察者,这是预期的行为,每个*toggleCollapsibleContent实例一个。

但是,如果我转到另一个路线并渲染另一个组件,依此类推,togglerState$主题仍然存在,当我返回到我/home路线HomeComponent时加载后,togglerState $增加了2个观察者,因为原来的观察者仍在那里,现在我有4个观察者,每个*toggleCollapsibleContent指令实例2个,因此我的内容会重复。

有谁知道为什么会这样?

1 个答案:

答案 0 :(得分:0)

您需要明确取消订阅:

    this.subscription = this._toggle.togglerState$.subscribe(status => { ...

...

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