Спроба використовувати зруйнований вигляд: виявити зміни


79

Я створюю простий інтерфейс, використовуючи Angular Meteor 2.

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.

            }

        });
    }
}

коментувати виклик для виявлення змін (); функція і перевірити, де це генерує якусь іншу помилку
Mayur

У мене така ж проблема з тим самим сценарієм.
Аль-Мотафар

Відповіді:


97

Єдине рішення, яке працювало для мене:

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

1
Те саме тут, дякую! Хоча я вирішив додати приватне поле isDestroyedв кожен компонент там, де це було потрібно (тобто там, де скасування підписки було недостатньо). Я встановив для поля значення true у ngOnDestroy і використовував його, а не намагався отримати доступ до внутрішнього поля дефектоскопа змін.
dbandstra

2
Хоча ця відповідь не дозволяє помилці підірвати ваш додаток, вона охоплює першопричину. Al-Mothafar має правильне рішення тут , яке повинно від компонента , який більше не наданий. detachcdr
Метт Вестлейк

5
Це насправді працює! З іншого боку, це здається ДУЖЕ удалим, оскільки destroyedне є частиною загальнодоступного API changeDetectionRefоб’єкта. Чи є спосіб застосувати це без доступу до нього ['destroyed']?
lealceldeiro

2
Погодився з @lealceldeiro. З новішими версіями TypeScript ми більше не можемо цього робити.
krillgar

1
Ви можете використовувати , !(this.changeDetectionRef as ViewRef).destroyedщоб зробити його трохи менше Hacky , але це все ще досить Hacky в ці дні
electrovir

86

Я вирішив ту ж проблему, що і ваша, але з набагато меншим кодом, я розповім вам те, що може допомогти вам вирішити проблему.

Проблема, очевидно, походить від того, 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 Коли слід відмовитись від підписки

Редагувати : я знаю інше рішення в Інтернеті, яке намагається просто вирішити проблему, обробляючи саму помилку, тоді як найкраща практика - знати корінь проблеми, перевірити, чи подання зруйновано чи ні, є хорошим рішенням, але першопричиною може бути проблемою, пов’язаною з витоком пам’яті, тому корінь проблеми полягає в тому, що запущену службу потрібно вбити, а НЕ просто спробувати вбити саму помилку без кореня, наприклад, запущені підписки (особливо ваша власна) повинні бути закриті.

ТАКЕ ведення домашнього господарства необхідне для кращої роботи, це не завжди є простішим і швидшим рішенням, тим кращим, якщо ви заховаєте бруд під килимами, це не означає, що ви прибрали свою кімнату :)


Коли я це зробив, я отримав цю помилку у функції ngOnDestory: console.js: 26 ПОМИЛКА Помилка: Uncaught (обіцяно): TypeError: Не вдається прочитати властивість 'detach' невизначеного TypeError: Не вдається прочитати властивість '
detach

це означає, що ChangeDetectorRefназва вашої послуги неправильна, можливо? якщо у вас є private cdRef: ChangeDetectorRefконструктор, він повинен працювати.
Аль-Мотафар

1
Чому метод ( detectChanges) буде викликаний під час фази знищення?
Донгбін Кім,

1
@DongBinKim Якщо ви ввімкнули detectChangesі зміни відбулися під час фази знищення, ця функція почне застосовувати зміни до мертвого компонента ще до того, як служби також загинуть, тому проблема трапиться, це щось пов’язане з Переглядом життєвого циклу для angular, у будь-якому випадку ви можете див. це посилання angular.io/api/core/ChangeDetectorRef#detectChanges та angular.io/guide/lifecycle-hooks, як ви можете бачити, як DOM залишається, і служба в пам'яті намагатиметься застосувати зміни, поки сам компонент загинув.
Аль-Мотафар

2
Це рішення для мене не спрацювало. Насправді коментар коду для методу detectChanges, схоже, припускає, що detectChanges знову приєднає детектор змін. Для мене єдиним робочим рішенням був метод tomaszbak, або завжди скасувати підписку / clearTimeout / тощо.
dbandstra

15

Можна використовувати

this.cdref.markForCheck();

замість this.cdref.detectChanges(); у багатьох випадках. Але найкращий підхід - дотримуватися порад @ Al-Mothafar



6

Просто від'єднайте ChangeDetectorRef від гачка життєвого циклу OnDestroy і перевірте, чи не було знищено ChangeDetectorRef перед виконанням методу detectChanges

    constructor(private cd: ChangeDetectorRef){}

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

    ngOnDestroy(){
      this.cd.detach();
    }

чи потрібно this.cd.detach () при знищенні?
KingMario

Коли компонент більше не відображається, ChangeDetectorRef можна від'єднати. Це не потрібно, але, роблячи це, ми переконуємось, що у зруйнованому поданні не відбувається виявлення змін. З іншого боку, якщо ви дійсно впевнені, що виявлення змін не потрібне або вам потрібно відключити виявлення змін у компоненті, ви можете від’єднати детектор змін у будь-який момент.
Тіліна Піядасун,

Дякую Тіліна. Думаю, this.cdбуде автоматично від’єднано при знищенні. І якщо це не так, оскільки !this.cd['destroyed']випробувано раніше this.cd.detectChanges()в someFunction, ви будете впевнено зателефонувати someFunctionза будь-яких обставин.
KingMario

5

Ви повинні отримати значення абонента в змінній і скасувати підписку на ту саму змінну. Будь ласка, зверніться до того ж коду

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 ().


4

Моє рішення полягало в тому, щоб скасувати передплату всіх спостерігачів.

Підписка:

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

Скасування підписки з changeDetector.detach ():

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

Це було необхідним для мого коду, я також повинен використовувати функцію ChangeDetectorRef, тільки ці дві речі без помилок одержали мій код.


2

У моєму випадку мова йшла про неправильне управління асинхронним тестовим налаштуванням конфігурації та компіляції компонентів.

  1. Невдалий код мав єдину асинхронізацію beforeEach () TypeScript Async / Await
  2. Щоб це працювало, я використовую два beforeEach (), де перший обробляє Async, використовуючи Angular's async () , а другий beforeEach () є синхронізацією .

Код, що спричиняє помилки ..

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();
});

2

Не дуже пов’язане з конкретним питанням, але я потрапив сюди, погугливши ту саму помилку, тому поділюсь своїм обхідним шляхом. Проблема полягала в тому, що я дзвонив fixture.detectChanges()всередину, fixture.whenStable().then(() => {})не переносячи тест у asyncфункцію.

До:

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

Після:

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

1

Просто:

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();
}

1

Моя причина трапилася, коли я намагався відкрити модальний діалог з NgbActiveModal. Мабуть, заклик .dismiss()із власних ngOnInit()причин викликає це.

export class MyModal {
    constructor(private modalInstance: NgbActiveModal) {
    }

    ngOnInit() {
       if (foo) this.modalInstance.dismiss();
    }

Рішенням було почекати галочку перед звільненням.

if (foo) setTimeout(() => this.modalInstance.dismiss());


1

Ну, ці відповіді мені не допомогли. Я знайшов інше рішення.

Дочірній компонент має вихід, який спрацьовує при натисканні кнопки закриття

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

А метод "hideChildComponent ()" у батьківському компоненті виявляє зміни.

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

Сподіваюся, це комусь допоможе.


1

Для мене єдиним рішенням, яке спрацювало, є наступне:

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 (), анулюйте передплату, встановіть для прапору "знищений компонент" значення, що відповідає дійсності, та від'єднайте changeDetector.

1

Щоб уникнути цієї помилки, замість виклику deteChanges () спробуйте обернути код, що змінює модель:

this.ngZone.run(() => {
      ...
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.