fix(ivy): support checkNoChanges on embedded views (#28644)
Before this fix our ViewRef implementation assumed that checkNoChanges can be only called on component views. In reality checkNoChanges can be also called on embedded views (ex.: when an embedded view is attached to ApplicationRef). PR Close #28644
This commit is contained in:
parent
2bf0d1a56f
commit
e5861e1c79
@ -2751,7 +2751,7 @@ function tickRootContext(rootContext: RootContext) {
|
|||||||
* @param component The component which the change detection should be performed on.
|
* @param component The component which the change detection should be performed on.
|
||||||
*/
|
*/
|
||||||
export function detectChanges<T>(component: T): void {
|
export function detectChanges<T>(component: T): void {
|
||||||
const view = getComponentViewByInstance(component) !;
|
const view = getComponentViewByInstance(component);
|
||||||
detectChangesInternal<T>(view, component);
|
detectChangesInternal<T>(view, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2790,9 +2790,14 @@ export function detectChangesInRootView(lView: LView): void {
|
|||||||
* introduce other changes.
|
* introduce other changes.
|
||||||
*/
|
*/
|
||||||
export function checkNoChanges<T>(component: T): void {
|
export function checkNoChanges<T>(component: T): void {
|
||||||
|
const view = getComponentViewByInstance(component);
|
||||||
|
checkNoChangesInternal<T>(view, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkNoChangesInternal<T>(view: LView, context: T) {
|
||||||
setCheckNoChangesMode(true);
|
setCheckNoChangesMode(true);
|
||||||
try {
|
try {
|
||||||
detectChanges(component);
|
detectChangesInternal(view, context);
|
||||||
} finally {
|
} finally {
|
||||||
setCheckNoChangesMode(false);
|
setCheckNoChangesMode(false);
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detec
|
|||||||
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
|
||||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
||||||
|
|
||||||
import {checkNoChanges, checkNoChangesInRootView, checkView, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
|
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions';
|
||||||
import {TNode, TNodeType, TViewNode} from './interfaces/node';
|
import {TNode, TNodeType, TViewNode} from './interfaces/node';
|
||||||
import {FLAGS, HOST, LView, LViewFlags, PARENT, RENDERER_FACTORY, T_HOST} from './interfaces/view';
|
import {FLAGS, HOST, LView, LViewFlags, PARENT, T_HOST} from './interfaces/view';
|
||||||
import {destroyLView} from './node_manipulation';
|
import {destroyLView} from './node_manipulation';
|
||||||
import {getNativeByTNode} from './util';
|
import {getNativeByTNode} from './util';
|
||||||
|
|
||||||
@ -252,7 +252,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
|||||||
* This is used in development mode to verify that running change detection doesn't
|
* This is used in development mode to verify that running change detection doesn't
|
||||||
* introduce other changes.
|
* introduce other changes.
|
||||||
*/
|
*/
|
||||||
checkNoChanges(): void { checkNoChanges(this.context); }
|
checkNoChanges(): void { checkNoChangesInternal(this._lView, this.context); }
|
||||||
|
|
||||||
attachToViewContainerRef(vcRef: viewEngine_ViewContainerRef) {
|
attachToViewContainerRef(vcRef: viewEngine_ViewContainerRef) {
|
||||||
if (this._appRef) {
|
if (this._appRef) {
|
||||||
|
71
packages/core/test/acceptance/change_detection_spec.ts
Normal file
71
packages/core/test/acceptance/change_detection_spec.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import {ApplicationRef, Component, Directive, EmbeddedViewRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
|
||||||
|
describe('change detection', () => {
|
||||||
|
|
||||||
|
describe('embedded views', () => {
|
||||||
|
|
||||||
|
@Directive({selector: '[viewManipulation]', exportAs: 'vm'})
|
||||||
|
class ViewManipulation {
|
||||||
|
constructor(
|
||||||
|
private _tplRef: TemplateRef<{}>, private _vcRef: ViewContainerRef,
|
||||||
|
private _appRef: ApplicationRef) {}
|
||||||
|
|
||||||
|
insertIntoVcRef() { this._vcRef.createEmbeddedView(this._tplRef); }
|
||||||
|
|
||||||
|
insertIntoAppRef(): EmbeddedViewRef<{}> {
|
||||||
|
const viewRef = this._tplRef.createEmbeddedView({});
|
||||||
|
this._appRef.attachView(viewRef);
|
||||||
|
return viewRef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'test-cmp',
|
||||||
|
template: `
|
||||||
|
<ng-template #vm="vm" viewManipulation>{{'change-detected'}}</ng-template>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class TestCmpt {
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({declarations: [TestCmpt, ViewManipulation]});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect changes for embedded views inserted through ViewContainerRef', () => {
|
||||||
|
const fixture = TestBed.createComponent(TestCmpt);
|
||||||
|
const vm = fixture.debugElement.childNodes[0].references['vm'] as ViewManipulation;
|
||||||
|
|
||||||
|
vm.insertIntoVcRef();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(fixture.nativeElement).toHaveText('change-detected');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect changes for embedded views attached to ApplicationRef', () => {
|
||||||
|
const fixture = TestBed.createComponent(TestCmpt);
|
||||||
|
const vm = fixture.debugElement.childNodes[0].references['vm'] as ViewManipulation;
|
||||||
|
|
||||||
|
const viewRef = vm.insertIntoAppRef();
|
||||||
|
|
||||||
|
// A newly created view was attached to the CD tree via ApplicationRef so should be also
|
||||||
|
// change detected when ticking root component
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(viewRef.rootNodes[0]).toHaveText('change-detected');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -426,7 +426,7 @@
|
|||||||
"name": "callHooks"
|
"name": "callHooks"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "checkNoChanges"
|
"name": "checkNoChangesInternal"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "checkNoChangesMode"
|
"name": "checkNoChangesMode"
|
||||||
@ -527,9 +527,6 @@
|
|||||||
{
|
{
|
||||||
"name": "detachView"
|
"name": "detachView"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "detectChanges"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "detectChangesInternal"
|
"name": "detectChangesInternal"
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user