【问题标题】:Attempt to use a destroyed view: detectChanges尝试使用被破坏的视图:detectChanges
【发布时间】:2016-10-17 09:30:18
【问题描述】:

我正在使用 Angular Meteor 2 创建一个简单的 UI。

1) 我有一个顶部导航栏组件,它有一个“注销”按钮。
2) 单击“注销”按钮时,它会重定向到“登录”。
3)然后我在控制台看到这个错误:EXCEPTION: Attempt to use a destroyed view: detectChanges

例外:

EXCEPTION: Attempt to use a destroyed view: detectChanges
browser_adapter.js:77 EXCEPTION: Attempt to use a destroyed view: detectChangesBrowserDomAdapter.logError @ browser_adapter.js:77BrowserDomAdapter.logGroup @ browser_adapter.js:87ExceptionHandler.call @ exception_handler.js:57(anonymous function) @ application_ref.js:265schedulerFn @ async.js:123SafeSubscriber.__tryOrUnsub @ Subscriber.js:225SafeSubscriber.next @ Subscriber.js:174Subscriber._next @ Subscriber.js:124Subscriber.next @ Subscriber.js:88Subject._finalNext @ Subject.js:128Subject._next @ Subject.js:120Subject.next @ Subject.js:77EventEmitter.emit @ async.js:112onError @ ng_zone.js:120onHandleError @ ng_zone_impl.js:66ZoneDelegate.handleError @ angular2-polyfills.js:394Zone.runTask @ angular2-polyfills.js:323ZoneTask.invoke @ angular2-polyfills.js:490
browser_adapter.js:77 STACKTRACE:BrowserDomAdapter.logError @ browser_adapter.js:77ExceptionHandler.call @ exception_handler.js:59(anonymous function) @ application_ref.js:265schedulerFn @ async.js:123SafeSubscriber.__tryOrUnsub @ Subscriber.js:225SafeSubscriber.next @ Subscriber.js:174Subscriber._next @ Subscriber.js:124Subscriber.next @ Subscriber.js:88Subject._finalNext @ Subject.js:128Subject._next @ Subject.js:120Subject.next @ Subject.js:77EventEmitter.emit @ async.js:112onError @ ng_zone.js:120onHandleError @ ng_zone_impl.js:66ZoneDelegate.handleError @ angular2-polyfills.js:394Zone.runTask @ angular2-polyfills.js:323ZoneTask.invoke @ angular2-polyfills.js:490
browser_adapter.js:77 Error: Attempt to use a destroyed view: detectChanges
    at ViewDestroyedException.BaseException [as constructor] (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:3349:23)
    at new ViewDestroyedException (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:10626:16)
    at DebugAppView.AppView.throwDestroyedError (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:11277:72)
    at DebugAppView.AppView.detectChanges (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:11230:18)
    at DebugAppView.detectChanges (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:11321:44)
    at ViewRef_.detectChanges (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:11011:65)
    at http://localhost:3000/app/app.js?hash=323b1216814e80ed467d95bcda255eb217d7c468:2224:23
    at ZoneDelegate.invokeTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4721:174)
    at Object.onInvokeTask (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:9393:41)
    at ZoneDelegate.invokeTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4721:72)
  -------------   Elapsed: 80 ms; At: Wed Jun 15 2016 20:22:09 GMT-0700 (PDT)   -------------  
    at Object.onScheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5734:30)
    at ZoneDelegate.scheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4704:57)
    at Zone.scheduleMacroTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4652:47)
    at http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4467:37
    at setTimeout (eval at createNamedFn (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5346:24), <anonymous>:3:37)
    at new TopNavbarComponent (http://localhost:3000/app/app.js?hash=323b1216814e80ed467d95bcda255eb217d7c468:2221:9)
    at DebugAppView._View_HomeComponent0.createInternal (HomeComponent.template.js:48:34)
    at DebugAppView.AppView.create (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:11098:21)
  -------------   Elapsed: 2 ms; At: Wed Jun 15 2016 20:22:09 GMT-0700 (PDT)   -------------  
    at Object.onScheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5734:30)
    at ZoneDelegate.scheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4704:57)
    at Zone.scheduleMicroTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4649:47)
    at scheduleResolveOrReject (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4928:22)
    at resolvePromise (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4893:29)
    at http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4930:25
    at ZoneDelegate.invokeTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4721:174)
    at Object.onInvokeTask (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:9393:41)
  -------------   Elapsed: 0 ms; At: Wed Jun 15 2016 20:22:09 GMT-0700 (PDT)   -------------  
    at Object.onScheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5734:30)
    at ZoneDelegate.scheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4704:57)
    at Zone.scheduleMicroTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4649:47)
    at scheduleResolveOrReject (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4928:22)
    at ZoneAwarePromise.then (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5011:25)
    at RuntimeCompiler.resolveComponent (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:40230:14)
    at DynamicComponentLoader_.loadNextToLocation (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:10788:31)
    at RouterOutlet.activate (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:26844:26)
  -------------   Elapsed: 0 ms; At: Wed Jun 15 2016 20:22:09 GMT-0700 (PDT)   -------------  
    at Object.onScheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5734:30)
    at ZoneDelegate.scheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4704:57)
    at Zone.scheduleMicroTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4649:47)
    at scheduleResolveOrReject (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4928:22)
    at resolvePromise (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4893:29)
    at http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4877:21
    at ZoneDelegate.invoke (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4696:161)
    at Object.onInvoke (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:9402:41)
  -------------   Elapsed: 0 ms; At: Wed Jun 15 2016 20:22:09 GMT-0700 (PDT)   -------------  
    at Object.onScheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5734:30)
    at ZoneDelegate.scheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4704:57)
    at Zone.scheduleMicroTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4649:47)
    at scheduleResolveOrReject (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4928:22)
    at resolvePromise (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4893:29)
    at http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4877:21
    at ZoneDelegate.invoke (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4696:161)
    at Object.onInvoke (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:9402:41)
  -------------   Elapsed: 0 ms; At: Wed Jun 15 2016 20:22:09 GMT-0700 (PDT)   -------------  
    at Object.onScheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5734:30)
    at ZoneDelegate.scheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4704:57)
    at Zone.scheduleMicroTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4649:47)
    at scheduleResolveOrReject (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4928:22)
    at resolvePromise (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4893:29)
    at http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4930:25
    at ZoneDelegate.invokeTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4721:174)
    at Object.onInvokeTask (http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:9393:41)
  -------------   Elapsed: 1 ms; At: Wed Jun 15 2016 20:22:09 GMT-0700 (PDT)   -------------  
    at Object.onScheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5734:30)
    at ZoneDelegate.scheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4704:57)
    at Zone.scheduleMicroTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4649:47)
    at scheduleResolveOrReject (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4928:22)
    at ZoneAwarePromise.then (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5011:25)
    at http://localhost:3000/packages/modules.js?hash=560db94ec01c0b3e8f499491ffcce7a2ec6c3c5e:26895:53
    at http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:1105:22
    at ZoneDelegate.invoke (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4696:161)
  -------------   Elapsed: 0 ms; At: Wed Jun 15 2016 20:22:09 GMT-0700 (PDT)   -------------  
    at Object.onScheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:5734:30)
    at ZoneDelegate.scheduleTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4704:57)
    at Zone.scheduleMicroTask (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4649:47)
    at scheduleResolveOrReject (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4928:22)
    at resolvePromise (http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4893:29)
    at http://localhost:3000/packages/barbatus_angular2-runtime.js?hash=fda9b73362c52e988ad030102a9f58e4d584cda3:4877:21
    at http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:1105:22BrowserDomAdapter.logError @ browser_adapter.js:77ExceptionHandler.call @ exception_handler.js:60(anonymous function) @ application_ref.js:265schedulerFn @ async.js:123SafeSubscriber.__tryOrUnsub @ Subscriber.js:225SafeSubscriber.next @ Subscriber.js:174Subscriber._next @ Subscriber.js:124Subscriber.next @ Subscriber.js:88Subject._finalNext @ Subject.js:128Subject._next @ Subject.js:120Subject.next @ Subject.js:77EventEmitter.emit @ async.js:112onError @ ng_zone.js:120onHandleError @ ng_zone_impl.js:66ZoneDelegate.handleError @ angular2-polyfills.js:394Zone.runTask @ angular2-polyfills.js:323ZoneTask.invoke @ angular2-polyfills.js:490
Subscriber.js:229 Uncaught Attempt to use a destroyed view: detectChanges

top-navbar.component.ts

"use strict";
import {Logger} from "../services/logger.service";
import {Component, ChangeDetectionStrategy, ChangeDetectorRef} from '@angular/core';
import {User} from "../models/user";
import {Router} from '@angular/router-deprecated';
import {UserService} from "../services/user.service";
import {CORE_DIRECTIVES} from '@angular/common';
import {DROPDOWN_DIRECTIVES} from '../../node_modules/ng2-bootstrap';

@Component({
    selector: 'top-navbar',
    templateUrl: 'client/top-navbar/top-navbar.html',
    bindings: [UserService, Logger],
    directives: [CORE_DIRECTIVES, DROPDOWN_DIRECTIVES]
})

export class TopNavbarComponent {

    public user:User;

    public statusDropdown = {
        isOpen: false
    };

    constructor(private userService:UserService, private router:Router, private logger:Logger, private ref:ChangeDetectorRef) {
        setTimeout(() => {
            this.ref.markForCheck();
            this.user = this.userService.getLoggedInUser();
            this.ref.detectChanges();
        }, 0)
    }

    logout() {
        this.logger.warn('[Top Navbar] Logging out the user.');
        localStorage.clear();
        this.router.navigateByUrl('/login');
    }
}

这是我的 login.component.ts

"use strict";
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { FormBuilder, ControlGroup, Validators } from '@angular/common';
import { MeteorComponent } from 'angular2-meteor';
import { Router } from '@angular/router-deprecated';
import { Logger } from "../services/logger.service";

@Component({
    selector: 'login',
    templateUrl: 'client/login/login.html',
    changeDetection: ChangeDetectionStrategy.OnPush,
    bindings: [Logger]
})

export class LoginComponent extends MeteorComponent {

    loginForm:ControlGroup;
    loginFailed = false;

    constructor(private _logger:Logger, private _router:Router, private ref:ChangeDetectorRef) {
        super();
        let fb = new FormBuilder();
        this.loginForm = fb.group({
            username: ["", Validators.required],
            password: ["", Validators.required]
        });
    }

    login() {


        this.call('authenticateUser', this.loginForm.value.username, this.loginForm.value.password, (err, data) => {

            if (err) {
                this._logger.error(err);

            } else {
                this._logger.info('[Authentication API] ', data);

                if (data.status != 'LOGIN_SUCCESS') {
                    this.loginFailed = true;

                } else {
                    this.loginFailed = false    ;
                    var user = {
                        id: data.id,
                        name: data.name,
                        role: data.role
                    }
                    localStorage.setItem('user', JSON.stringify(user));
                    this._router.navigate(['Home'])
                }
                //This is required for letting Angular know that something has changed.
                //Because this part of code runs out of Angular zone.
                this.ref.markForCheck();  // Mark this component and its children for change detection in next detecting cycle.
                this.ref.detectChanges(); // Trigger change detection.

            }

        });
    }
}

【问题讨论】:

  • 评论detectChanges()的调用;函数并检查它在哪里产生一些其他错误
  • 同样的场景我也有同样的问题。

标签: angular angular2-meteor


【解决方案1】:

唯一对我有用的解决方案是:

if (!this.changeDetectionRef['destroyed']) {
    this.changeDetectionRef.detectChanges();
}

【讨论】:

  • 这里也一样,谢谢!虽然我选择在每个需要它的组件中添加一个私有字段isDestroyed(即取消订阅还不够)。我在 ngOnDestroy 中将该字段设置为 true,并使用它而不是尝试访问更改检测器 ref 的内部字段。
  • 虽然这个答案可以防止错误炸毁您的应用程序,但它涵盖了根本原因。 Al-Mothafar 在这里有正确的解决方案,即从不再渲染的组件中将detach cdr
  • 这确实有效!另一方面,这似乎很 hacky,因为 destroyed 不是 changeDetectionRef 对象的公共 API 的一部分。有没有办法在不通过['destroyed'] 加入的情况下应用它?
  • 同意@lealceldeiro。使用较新版本的 TypeScript,我们无法再这样做了。
  • 你可以使用!(this.changeDetectionRef as ViewRef).destroyed 让它不那么老套,但现在它仍然很老套
【解决方案2】:

我解决了与您相同的问题,但使用更小的代码,我会告诉您可以帮助您解决问题的要点。

问题显然来自detectChanges(),因为更改已完成,并且在组件的销毁阶段调用了方法。

所以你需要将你的组件设置为implements OnDestroy,然后你需要取消使this.ref.detectChanges()被调用的更改。所以你的TopNavbarComponent 必须类似于:

export class TopNavbarComponent implements OnDestroy {
  // ... your code

  ngOnDestroy() {
    this.cdRef.detach(); // do this

    // for me I was detecting changes using `detectChanges()` inside a subscription with `subscribe()` to observable without limitation, so was enough for me to just `unsubscribe()` like the following line;
    // this.authObserver.unsubscribe();
  }
}

P.S:不要忘记 unsubscribe() 您组件中的所有观察者!无论如何,您必须这样做,没有取消订阅的订阅可能是包括此在内的数百个问题的主要原因,请参阅Angular/RxJs When should I unsubscribe from `Subscription`

编辑: 我知道网络上的其他解决方案试图通过处理错误本身来解决问题,而最佳做法是了解问题的根源,检查视图是否被破坏是一个很好的解决方案恕我直言,因为最初的原因可能是内存泄漏背后的问题,谁知道呢!所以问题的根源是需要终止正在运行的服务,而不是在不知道问题根源的情况下尝试终止错误本身,例如,必须关闭正在运行的订阅(尤其是您的自定义订阅)。

所以!家务对于更好的性能是必要的,它并不总是一个更容易和更快的解决方案是更好的解决方案,如果你把污垢藏在地毯下并不意味着你“清理”了你的房间,即使它看起来确实不错:)

【讨论】:

  • 当我这样做时,我在 ngOnDestory 函数处收到此错误:console.js:26 错误错误:未捕获(承诺):TypeError:无法读取未定义的属性“分离”类型错误:无法读取属性未定义的“分离”
  • 这意味着您的ChangeDetectorRef 服务名称可能不正确?如果你在构造函数中有private cdRef: ChangeDetectorRef 它应该可以工作。
  • 为什么要在销毁阶段调用方法(detectChanges)?
  • @DongBinKim 如果你打开了detectChanges 并且在销毁阶段发生了变化,这个函数也会在服务死掉之前开始对死组件应用变化,所以问题就会发生,它是与角度视图生命周期相关的东西,无论如何你可以看到这个链接angular.io/api/core/ChangeDetectorRef#detectChangesangular.io/guide/lifecycle-hooks,因为你可以看到DOM停留并且内存中的服务将在组件本身被杀死时尝试应用更改。
  • 这个解决方案对我不起作用。实际上,detectChanges 方法的代码注释似乎表明 detectChanges 将重新附加更改检测器。对我来说,唯一可行的解​​决方案是 tomaszbak 的方法,或者总是取消订阅/clearTimeout/等。
【解决方案3】:

你可以使用

this.cdref.markForCheck();

而不是 this.cdref.detectChanges(); 在很多情况下。但最好的方法是遵循@Al-Mothafar 提示

【讨论】:

    【解决方案4】:

    我已经解决了:

    if (!(<ViewRef>this.cd).destroyed) {
       this.cd.detectChanges();
    }
    

    【讨论】:

    • 与接受的答案几乎相同,但提供了现在必要的类型转换
    【解决方案5】:

    只需在 OnDestroy 生命周期钩子上分离 ChangeDetectorRef 并在执行 detectChanges 方法之前检查 ChangeDetectorRef 是否已销毁

        constructor(private cd: ChangeDetectorRef){}
    
        someFunction(){
          if(!this.cd['destroyed']){
            this.cd.detectChanges();
          }
        }
    
        ngOnDestroy(){
          this.cd.detach();
        }
    

    【讨论】:

    • 是否需要 this.cd.detach() 销毁?
    • 当组件不再渲染时,可以分离 ChangeDetectorRef。这不是必需的,但这样做我们确保在被破坏的视图中没有发生任何更改检测。另一方面,如果您确实确信不需要更改检测或需要禁用组件中的更改检测,则可以随时分离更改检测器。
    • 谢谢蒂琳娜。我认为this.cd 将在销毁时自动分离。如果不是这样,由于 !this.cd['destroyed']someFunction 中的 this.cd.detectChanges() 之前测试,因此您在任何情况下都可以放心调用 someFunction
    【解决方案6】:

    您必须在一个变量中获取订阅者的值并通过同一个变量取消订阅。请参考以下相同的代码

    import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy } from '@angular/core';
    import { Observable } from 'rxjs/Observable';
    import 'rxjs/add/observable/of';
    import { Cartservice } from './../cartservice.service';
    import { ISubscription } from 'rxjs/Subscription';
    
    
    
    export class CartComponent implements OnInit, OnDestroy {
    
    private subscription: ISubscription;
    ngOnInit() {
        this.subscription = this.cartservice.productsObservable.subscribe(cart => {
          this.cartProducts = cart.products;
          this.cartTotal = cart.cartTotal;
          this.changeDetectorRef.detectChanges();
        });
      }
    
     ngOnDestroy() {
        this.subscription.unsubscribe();
      }
    }
    

    请注意,我在方法 ngOnDestroy() 中取消订阅更改。

    【讨论】:

      【解决方案7】:

      我的解决方案是取消订阅所有观察者。

      订阅:

      ngOnInit() {
           this._currentUserSubscription = this._auth.currentUser.subscribe(currentUser => {});
      }
      

      使用 changeDetector.detach() 取消订阅:

      ngOnDestroy() {
          this._currentUserSubscription.unsubscribe();
          this._cdRef.detach();
      }
      

      这对我的代码来说是必需的,我也必须使用 ChangeDetectorRef 功能,只有这两件事才能使我的代码没有错误。

      【讨论】:

        【解决方案8】:

        就我而言,这是一个对组件配置和编译的异步测试设置管理不善的问题。

        1. 失败的代码在使用 TypeScript Async/Await 之前有一个单一的 async beforeEach()
        2. 为了让它工作,我使用了两个 beforeEach()s,其中第一个使用 Angular's async()second beforeEach() is sync 处理异步。

        导致错误的代码..

        beforeEach(async () => {
            await TestBed.configureTestingModule({
                imports: [
                    BrowserAnimationsModule
                ],
                providers: [
                    { provide: ComponentFixtureAutoDetect, useValue: true },
                    { provide: OptionsService, useValue: optionServiceMock },
                ],
                declarations: [EventLogFilterComponent],
                schemas: [NO_ERRORS_SCHEMA]
            }).compileComponents();
            fixture = TestBed.createComponent(EventLogFilterComponent);
            component = fixture.componentInstance;
            optionsService = TestBed.get(OptionsService);
            component.filterElem = jasmine.createSpyObj('filterElem', ['close']);
            fixture.detectChanges();
        });
        

        已修复...

        beforeEach(async(() => {
            TestBed.configureTestingModule({
                imports: [
                    BrowserAnimationsModule
                ],
                providers: [
                    { provide: ComponentFixtureAutoDetect, useValue: true },
                    { provide: OptionsService, useValue: optionServiceMock },
                ],
                declarations: [EventLogFilterComponent],
                schemas: [NO_ERRORS_SCHEMA]
            }).compileComponents();
        }));
        
        beforeEach(() => {
            fixture = TestBed.createComponent(EventLogFilterComponent);
            component = fixture.componentInstance;
            optionsService = TestBed.get(OptionsService);
            component.filterElem = jasmine.createSpyObj('filterElem', ['close']);
            fixture.detectChanges();
        });
        

        【讨论】:

          【解决方案9】:

          与具体问题没有太大关系,但我通过谷歌搜索到了同样的错误,所以我将分享我的解决方法。问题是我在 fixture.whenStable().then(() =&gt; {}) 内部调用了 fixture.detectChanges() 而没有将测试包装在 async 函数中。

          之前:

          it('should...', () => {
            fixture.whenStable().then(() => {
              fixture.detectChanges();
            });
          });
          

          之后:

          it('should...', async(() => {
            fixture.whenStable().then(() => {
              fixture.detectChanges();
            });
          }));
          

          【讨论】:

            【解决方案10】:

            当我尝试使用 NgbActiveModal 打开一个模式对话框时,我的原因发生了。显然从它自己的ngOnInit() 调用.dismiss() 会导致这种情况。

            export class MyModal {
                constructor(private modalInstance: NgbActiveModal) {
                }
            
                ngOnInit() {
                   if (foo) this.modalInstance.dismiss();
                }
            

            解决方案是在解雇前稍等片刻。

            if (foo) setTimeout(() =&gt; this.modalInstance.dismiss());

            【讨论】:

              【解决方案11】:

              对我来说,唯一有效的解决方案如下:

              ngOnInit() {
                  if (this.destroyedComponent) this.changeDetector.reattach();
              
              this.destroyedComponent = false;
              this.subscription = this.reactive.channel$.subscribe(msg => {
                switch (msg) {
                  case "config:new_data":
                    if (!this.destroyedComponent) {
                      this.table.initTable();
                      this.changeDetector.detectChanges();
                    }
                }
              })
              }
              
              ngOnDestroy() {
                 this.subscription = null;
                 this.destroyedComponent = true;
                 this.changeDetector.detach();
              }
              

              解释:

              1. 如果组件之前已损坏,请重新连接检测器。
              2. 将之前的标志设置为虚假值。
              3. 保存 RxJs 订阅,并在其中设置所需的逻辑。
              4. 将该逻辑包装在一个条件中,以检查组件是否已被先前声明的标志设置为已销毁。
              5. 对该块内的更改执行所需的检测。
              6. 设置 ngOnDestroy() 方法并取消订阅,将 destroyComponent 标志设置为真值,并分离 changeDetector。

              【讨论】:

              • 这里相同; changeDetector.detach()changeDetector.markForCheck() 在我的代码中不起作用,所以我最终在 ngOnDestroy() 中设置了一个标志。
              【解决方案12】:

              为避免此错误,请尝试通过以下方式包装模型更改代码,而不是调用 detectChanges():

              this.ngZone.run(() => {
                    ...
              });
              

              【讨论】:

                【解决方案13】:

                简单:

                import { OnDestroy } from '@angular/core';
                import { Subject } from 'rxjs/Subject';
                
                export class Component implements OnDestroy {
                    componentDestroyed: Subject<boolean> = new Subject();
                
                constructor() { }
                
                function() {
                   this.service.serviceFunction
                     .takeUntil(this.componentDestroyed)
                     .subscribe((incomingObservable: Type) => {
                       this.variable = incomingObservable;
                     });
                  }
                
                ngOnDestroy() {
                   this._cdRef.detach(); //If you use change dectector
                   this.componentDestroyed.next(true);
                   this.componentDestroyed.complete();
                }
                

                【讨论】:

                  【解决方案14】:

                  嗯,这个答案对我没有帮助。我找到了其他解决方案。

                  子组件具有在单击关闭按钮时触发的输出

                  <child-component
                      *ngIf="childComponentIsShown"
                      (formCloseEmitter)="hideChildComponent()"
                  ></child-component>
                  

                  并且父组件中的“hideChildComponent()”方法检测到变化。

                  hideChildComponent() {
                    this.childComponentIsShown = false;
                    this.cdr.detectChanges();
                  }
                  

                  希望这会对某人有所帮助。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2017-10-23
                    • 1970-01-01
                    • 2017-12-13
                    • 2023-03-14
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-10-23
                    相关资源
                    最近更新 更多