Попытка использовать уничтоженное представление: detectChanges
Я создаю простой пользовательский интерфейс, используя Angular Meteor 2.
1) У меня есть верхний компонент navbar, в котором есть кнопка "Выход".
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
топ-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.
}
});
}
}
15 ответов
Единственное решение, которое сработало для меня, было:
if (!this.changeDetectionRef['destroyed']) {
this.changeDetectionRef.detectChanges();
}
Я решил ту же проблему, что и ваша, но с гораздо меньшим кодом, я расскажу вам о том, что может помочь вам решить проблему.
Проблема явно исходит от detectChanges()
потому что изменения были сделаны, и метод вызван во время фазы уничтожения компонента.
Так что вам нужно сделать ваш компонент implements OnDestroy
, то вам нужно отменить изменения, которые делают this.ref.detectChanges()
быть названным. Так что ваши TopNavbarComponent
должен быть похож на:
export class TopNavbarComponent implements OnDestroy {
// ... your code
ngOnDestroy() {
this.cdRef.detach(); // do this
// for me I was detect changes inside "subscribe" so was enough for me to just unsubscribe;
// this.authObserver.unsubscribe();
}
}
PS: не забудьте unsubscribe()
все наблюдатели, которые у вас есть в вашем компоненте! В любом случае, вы должны сделать это, подписка без отписки может быть основной причиной сотен проблем, включая эту, см. Angular/RxJs Когда мне следует отписаться от `Subscription`
Изменить: я знаю другое решение в Интернете, пытающееся просто решить проблему, обрабатывая саму ошибку, в то время как лучшая практика заключается в том, чтобы узнать корень проблемы, проверить, является ли представление уничтоженным или нет, является хорошим решением, но первоначальной причиной Это может быть причиной утечки памяти, поэтому корень проблемы в том, что работающий сервис необходимо уничтожить, НЕ просто пытаться уничтожить саму ошибку без рута, например, закрытые подписки (особенно пользовательские) должны быть закрыты.
Так что хозяйственные вещи необходимы для лучшей производительности, это не всегда более простое и быстрое решение, лучше, если вы прячете грязь под коврами, это не значит, что вы убрали свою комнату:)
Ты можешь использовать
this.cdref.markForCheck();
вместоthis.cdref.detectChanges();
во многих случаях. Но лучший подход - следовать советам @Al-Mothafar
Я решил это с:
if (!(<ViewRef>this.cd).destroyed) {
this.cd.detectChanges();
}
Просто отсоедините ChangeDetectorRef на хуке жизненного цикла OnDestroy и проверьте, уничтожен ли ChangeDetectorRef перед выполнением метода deteChanges
constructor(private cd: ChangeDetectorRef){}
someFunction(){
if(!this.cd['destroyed']){
this.cd.detectChanges();
}
}
ngOnDestroy(){
this.cd.detach();
}
Вы должны получить значение подписчика в переменной и отписаться от той же переменной. Пожалуйста, обратитесь следующий код
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().
Моим решением было отписать всех наблюдателей.
Подписка:
ngOnInit() {
this._currentUserSubscription = this._auth.currentUser.subscribe(currentUser => {});
}
Отказаться от подписки с помощью changeDetector.detach():
ngOnDestroy() {
this._currentUserSubscription.unsubscribe();
this._cdRef.detach();
}
Это было необходимо в моем коде, я также должен использовать функциональность ChangeDetectorRef, только те две вещи, которые мой код без ошибок.
Моя причина произошла, когда я пытался открыть модальное диалоговое окно с NgbActiveModal. Видимо зовет .dismiss()
от своего собственного ngOnInit()
вызывает это.
export class MyModal {
constructor(private modalInstance: NgbActiveModal) {
}
ngOnInit() {
if (foo) this.modalInstance.dismiss();
}
Решением было подождать до увольнения.
if (foo) setTimeout(() => this.modalInstance.dismiss());
Для меня единственным решением, которое сработало, является следующее:
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();
}
Объяснение:
- Если компонент был ранее разрушен, снова подключите детектор.
- Установите этот предыдущий флаг в ложное значение.
- Сохраните подписку RxJs и установите желаемую логику внутри.
- Оберните эту логику в условие, которое проверяет, был ли компонент уничтожен или нет ранее объявленным флагом.
- Выполните желаемое обнаружение изменений внутри этого блока.
- Установите метод ngOnDestroy() и аннулируйте подписку, установите флаг destroyComponent в истинное значение и отсоедините changeDetector.
Чтобы избежать этой ошибки, вместо вызова detectChanges() попробуйте обернуть код изменения модели следующим образом:
this.ngZone.run(() => {
...
});
Не сильно связанный с конкретным вопросом, но я попал сюда, найдя ту же ошибку, поэтому поделюсь своим решением. Проблема была в том, что я звонил fixture.detectChanges()
внутри fixture.whenStable().then(() => {})
без упаковки теста в течение async
функция.
До:
it('should...', () => {
fixture.whenStable().then(() => {
fixture.detectChanges();
});
});
После:
it('should...', async(() => {
fixture.whenStable().then(() => {
fixture.detectChanges();
});
}));
В моем случае это была проблема неправильной настройки асинхронных тестов конфигурации и компиляции компонентов.
- Сбой кода имел особую асинхронность beforeEach() с использованием TypeScript Async/Await
- Чтобы заставить его работать, я использую два beforeEach(), где первый обрабатывает Async, используя async () Angular, а второй beforeEach() - 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();
});
Ну, эти ответы не помогли мне. Я нашел другое решение.
Дочерний компонент имеет вывод, который срабатывает при нажатии кнопки закрытия
<child-component
*ngIf="childComponentIsShown"
(formCloseEmitter)="hideChildComponent()"
></child-component>
И метод hideChildComponent() в родительском компоненте обнаруживает изменения.
hideChildComponent() {
this.childComponentIsShown = false;
this.cdr.detectChanges();
}
Надеюсь, это кому-нибудь поможет.
Просто:
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();
}
Попробуйте использовать setTimeout без параметра задержки. Для меня это сработало хорошо, потому что setTimout без задержки — хитрый способ обнаружить изменения после завершения обновления модели.
setTimeout(() => {
if (!this.ref['destroyed']) {
this.ref.detectChanges();
}
});