Skip to content
Permalink
Browse files

fix(ivy): do not invoke change detection for destroyed views (#34241)

Prior to this commit, calling change detection for destroyed views resulted in errors being thrown in some cases. This commit adds a check to make sure change detection is invoked for non-destroyed views only.

PR Close #34241
  • Loading branch information
AndrewKushnir committed Dec 4, 2019
1 parent af36bc6 commit 24bbcafe7848a617b514b27b53b9fbe431055c13
@@ -380,8 +380,9 @@ export function renderView<T>(lView: LView, tView: TView, context: T): void {
export function refreshView<T>(
lView: LView, tView: TView, templateFn: ComponentTemplate<{}>| null, context: T) {
ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
enterView(lView, lView[T_HOST]);
const flags = lView[FLAGS];
if ((flags & LViewFlags.Destroyed) === LViewFlags.Destroyed) return;
enterView(lView, lView[T_HOST]);
try {
resetPreOrderHookFlags(lView);

@@ -7,7 +7,7 @@
*/

import {CommonModule} from '@angular/common';
import {Component, ComponentFactoryResolver, Directive, Input, NgModule, OnChanges, SimpleChanges, ViewChild, ViewContainerRef} from '@angular/core';
import {ChangeDetectorRef, Component, ComponentFactoryResolver, ContentChildren, Directive, Input, NgModule, OnChanges, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
import {SimpleChange} from '@angular/core/src/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
@@ -3597,6 +3597,59 @@ describe('onDestroy', () => {
expect(events).toEqual(['comp']);
});

it('should not produce errors if change detection is triggered during ngOnDestroy', () => {
@Component({selector: 'child', template: `<ng-content></ng-content>`})
class Child {
}

@Component({selector: 'parent', template: `<ng-content></ng-content>`})
class Parent {
@ContentChildren(Child, {descendants: true}) child !: QueryList<Child>;
}

@Component({
selector: 'app',
template: `
<ng-template #tpl>
<parent>
<child></child>
</parent>
</ng-template>
<div #container dir></div>
`
})
class App {
@ViewChild('container', {read: ViewContainerRef, static: true})
container !: ViewContainerRef;

@ViewChild('tpl', {read: TemplateRef, static: true})
tpl !: TemplateRef<any>;

ngOnInit() { this.container.createEmbeddedView(this.tpl); }
}

@Directive({selector: '[dir]'})
class Dir {
constructor(public cdr: ChangeDetectorRef) {}

ngOnDestroy() {
// Important: calling detectChanges in destroy hook like that
// doesn’t have practical purpose, but in real-world cases it might
// happen, for example as a result of "focus()" call on a DOM element,
// in case ZoneJS is active.
this.cdr.detectChanges();
}
}

TestBed.configureTestingModule({
declarations: [App, Parent, Child, Dir],
});
const fixture = TestBed.createComponent(App);
fixture.autoDetectChanges();

expect(() => fixture.destroy()).not.toThrow();
});

onlyInIvy(
'View Engine has the opposite behavior, where it calls destroy on the directives first, then the components')
.it('should be called on directives after component', () => {

0 comments on commit 24bbcaf

Please sign in to comment.
You can’t perform that action at this time.