fix(ivy): make ViewRef.detectChanges work with embedded views (FW-749) (#27521)
In `ViewRef.detectChanges`, we are passing `ViewRef.context` into `detectChanges` to trigger change detection. This only makes sense for component `ViewRefs` (i.e. injected `ChangeDetectorRefs`) because with embedded views, `context` is not a component instance where the view has been monkey-patched. It's a just a normal object, so the view will be undefined. In order to resolve this problem, we now invoke `detectChangesInternal` and also pass `LView` (to make sure we always have a view available). PR Close #27521
This commit is contained in:
parent
16d26e5f38
commit
05cdfb90e9
|
@ -2517,7 +2517,7 @@ export function checkNoChangesInRootView(lView: LView): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */
|
/** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */
|
||||||
function detectChangesInternal<T>(hostView: LView, component: T, rf: RenderFlags | null) {
|
export function detectChangesInternal<T>(hostView: LView, component: T, rf: RenderFlags | null) {
|
||||||
const hostTView = hostView[TVIEW];
|
const hostTView = hostView[TVIEW];
|
||||||
const oldView = enterView(hostView, hostView[HOST_NODE]);
|
const oldView = enterView(hostView, hostView[HOST_NODE]);
|
||||||
const templateFn = hostTView.template !;
|
const templateFn = hostTView.template !;
|
||||||
|
|
|
@ -11,7 +11,7 @@ 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, detectChanges, detectChangesInRootView, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
|
import {checkNoChanges, checkNoChangesInRootView, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
|
||||||
import {TNode, TNodeType, TViewNode} from './interfaces/node';
|
import {TNode, TNodeType, TViewNode} from './interfaces/node';
|
||||||
import {FLAGS, HOST, HOST_NODE, LView, LViewFlags, PARENT, RENDERER_FACTORY} from './interfaces/view';
|
import {FLAGS, HOST, HOST_NODE, LView, LViewFlags, PARENT, RENDERER_FACTORY} from './interfaces/view';
|
||||||
import {destroyLView} from './node_manipulation';
|
import {destroyLView} from './node_manipulation';
|
||||||
|
@ -244,7 +244,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
||||||
if (rendererFactory.begin) {
|
if (rendererFactory.begin) {
|
||||||
rendererFactory.begin();
|
rendererFactory.begin();
|
||||||
}
|
}
|
||||||
detectChanges(this.context);
|
detectChangesInternal(this._lView, this.context, null);
|
||||||
if (rendererFactory.end) {
|
if (rendererFactory.end) {
|
||||||
rendererFactory.end();
|
rendererFactory.end();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateRef, ViewContainerRef} from '@angular/core';
|
import {EmbeddedViewRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
import {withBody} from '@angular/private/testing';
|
import {withBody} from '@angular/private/testing';
|
||||||
|
|
||||||
import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core';
|
import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core';
|
||||||
|
@ -91,13 +91,22 @@ describe('change detection', () => {
|
||||||
it('should support detectChanges on components that have LContainers', () => {
|
it('should support detectChanges on components that have LContainers', () => {
|
||||||
let structuralComp !: StructuralComp;
|
let structuralComp !: StructuralComp;
|
||||||
|
|
||||||
|
function FooTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
text(0);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
textBinding(0, bind(ctx.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class StructuralComp {
|
class StructuralComp {
|
||||||
tmp !: TemplateRef<any>;
|
tmp !: TemplateRef<any>;
|
||||||
value = 'one';
|
value = 'one';
|
||||||
|
|
||||||
constructor(public vcr: ViewContainerRef) {}
|
constructor(public vcr: ViewContainerRef) {}
|
||||||
|
|
||||||
create() { this.vcr.createEmbeddedView(this.tmp); }
|
create() { return this.vcr.createEmbeddedView(this.tmp, this); }
|
||||||
|
|
||||||
static ngComponentDef = defineComponent({
|
static ngComponentDef = defineComponent({
|
||||||
type: StructuralComp,
|
type: StructuralComp,
|
||||||
|
@ -107,32 +116,17 @@ describe('change detection', () => {
|
||||||
inputs: {tmp: 'tmp'},
|
inputs: {tmp: 'tmp'},
|
||||||
consts: 1,
|
consts: 1,
|
||||||
vars: 1,
|
vars: 1,
|
||||||
template: (rf: RenderFlags, ctx: StructuralComp) => {
|
template: FooTemplate
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
text(0);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
textBinding(0, bind(ctx.value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function FooTemplate(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
text(0, 'Temp content');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <ng-template #foo>
|
* <ng-template #foo>{{ value }}</ng-template>
|
||||||
* Temp content
|
|
||||||
* </ng-template>
|
|
||||||
* <structural-comp [tmp]="foo"></structural-comp>
|
* <structural-comp [tmp]="foo"></structural-comp>
|
||||||
*/
|
*/
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
template(0, FooTemplate, 1, 0, '', null, ['foo', ''], templateRefExtractor);
|
template(0, FooTemplate, 2, 1, '', null, ['foo', ''], templateRefExtractor);
|
||||||
element(2, 'structural-comp');
|
element(2, 'structural-comp');
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
|
@ -145,13 +139,19 @@ describe('change detection', () => {
|
||||||
fixture.update();
|
fixture.update();
|
||||||
expect(fixture.html).toEqual('<structural-comp>one</structural-comp>');
|
expect(fixture.html).toEqual('<structural-comp>one</structural-comp>');
|
||||||
|
|
||||||
structuralComp.create();
|
const viewRef: EmbeddedViewRef<any> = structuralComp.create();
|
||||||
fixture.update();
|
fixture.update();
|
||||||
expect(fixture.html).toEqual('<structural-comp>one</structural-comp>Temp content');
|
expect(fixture.html).toEqual('<structural-comp>one</structural-comp>one');
|
||||||
|
|
||||||
|
// check embedded view update
|
||||||
structuralComp.value = 'two';
|
structuralComp.value = 'two';
|
||||||
detectChanges(structuralComp);
|
viewRef.detectChanges();
|
||||||
expect(fixture.html).toEqual('<structural-comp>two</structural-comp>Temp content');
|
expect(fixture.html).toEqual('<structural-comp>one</structural-comp>two');
|
||||||
|
|
||||||
|
// check root view update
|
||||||
|
structuralComp.value = 'three';
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html).toEqual('<structural-comp>three</structural-comp>three');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue