Angular2子组件在ngFor中意外销毁

Posted

技术标签:

【中文标题】Angular2子组件在ngFor中意外销毁【英文标题】:Angular2 Child component destroyed unexpectedly in ngFor 【发布时间】:2017-01-21 03:16:08 【问题描述】:

所以我有一个带有 @Output 事件的 from 组件,该事件在提交时触发,如下所示:

@Component(
    selector: 'some-component',
    templateUrl: './SomeComponent.html'
)
export class SomeComponent    
    @Input() data: any;
    @Output() onSubmit: EventEmitter<void> = new EventEmitter<void>();

    constructor(private someService: SomeService) 

    submitForm(): void
        this.someService.updateBackend(this.data, ()=>
            this.onSubmit.emit();
        );
    

我正在使用ngFor 来创建此组件的多个元素:

<template let-data ngFor [ngForOf]="dataCollection">
    <some-component  [data]="data" (onSubmit)="doSomthing()"></some-component>
</template>

最后缺少的部分是提交时使用的服务:

@Injectable()
export class SomeService

    constructor() 

    updateBackend(data: any, callback: () => void): void
        /*
         * updating the backend
         */.then((result) => 
            const  errors, data  = result;

            if (data) 
                callback();
            
        )
    

submitForm() 函数的开头,this.onSubmit.observers 是一个包含一个观察者的数组,就像它应该的那样。

一旦到达回调方法,this.onSubmit.emit() 被调用,this.onSubmit.observers 就是一个包含零观察者的数组。

我遇到了两种非常奇怪的行为:

如果我删除了在SomeService.updateBackend 中更新后端的实际调用,它工作得非常好,observers 仍然是一个包含一个观察者的数组! 如果我保持对后端的实际调用但不使用ngFor 并且只显示一个&lt;some-element&gt; 它也可以正常工作,将this.onSubmit.observers 中的一个观察者保持在回调范围内!

知道我做错了什么吗?

提前致谢!

更新:

感谢@StevenLuke 关于记录SomeComponentngOnDestroy 的评论,我发现它在发射之前就被销毁了。

实际上,SomeService.updateBackend 结束后它做的第一件事就是销毁该组件的所有实例并重新创建它们!

这就是让观察者发生变化的原因!为什么会这样?

【问题讨论】:

我建议将标题更改为反映问题的内容。 this 根本没有改变。它是同一个对象。在 ngFor 中输出事件监听器。 @GünterZöchbauer 好的,我会尝试实施您的建议并在我完成后立即更新 当 observable 替换数组 *ngFor 迭代时,*ngFor 必须更新视图以反映模型中的更改。如果您只添加或删除项目,那么*ngFor 也只是添加删除 DOM 中的相关元素。 @GünterZöchbauer 由于我的 doSomething() 实际上是从后端重新获取数据,它正在替换绑定的 dataCollection 并导致 ngFor 完全重新初始化 抱歉,我刚刚看到您在问题中所做的更改。您的答案已经包含:D 【参考方案1】:

如果您在 *ngFor 中提供 trackBy 函数来识别 dataCollection 中的项目,它不会破坏和初始化。您的模板将是:

<some-component *ngFor="let data of dataCollection;trackBy:trackByFunction"
  [data]="data" (onSubmit)="doSomthing()"></some-component>

trackByFunction 看起来像:

trackByFunction(index, item) 
    return item ? item.id : undefined;

因此,即使 dataCollection 中的一个项目是一个新对象,如果它的 id 与上一个集合中的 id 匹配,*ngFor 将更新 [data] 但不会销毁并初始化组件。

【讨论】:

听起来很简单,不知道一开始怎么没有! :)【参考方案2】:

感谢@GünterZöchbauer cmets,我发现这种情况是ngFor 绑定的数据在我更新后端时被新实例替换,因此,它重新渲染了它的子组件,导致重新初始化(destory + init ) ,这使得组件的实例被覆盖。

为了解决这个问题,我不得不将dataCollection 放在一个单独的服务中,为父组件ngOnInit 获取它,避免导致ngFor 的重新渲染,并且只再次获取它的数据子组件执行结束后

希望对某人有所帮助!

【讨论】:

以上是关于Angular2子组件在ngFor中意外销毁的主要内容,如果未能解决你的问题,请参考以下文章

angular2你如何在`ngFor`中渲染一个计时器组件

Angular 2:包含子组件的表单

ngFor 中的 Angular2 组件抛出错误(viewFactory 不是函数)

如何销毁父组件内的子组件

Angular2 NgFor在表达式中绑定数组

Angular2:嵌套 *ngFor 导致“检查后表达式已更改”