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. */
|
||||
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 oldView = enterView(hostView, hostView[HOST_NODE]);
|
||||
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 {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 {FLAGS, HOST, HOST_NODE, LView, LViewFlags, PARENT, RENDERER_FACTORY} from './interfaces/view';
|
||||
import {destroyLView} from './node_manipulation';
|
||||
|
@ -244,7 +244,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
|||
if (rendererFactory.begin) {
|
||||
rendererFactory.begin();
|
||||
}
|
||||
detectChanges(this.context);
|
||||
detectChangesInternal(this._lView, this.context, null);
|
||||
if (rendererFactory.end) {
|
||||
rendererFactory.end();
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* 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 {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core';
|
||||
|
@ -91,13 +91,22 @@ describe('change detection', () => {
|
|||
it('should support detectChanges on components that have LContainers', () => {
|
||||
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 {
|
||||
tmp !: TemplateRef<any>;
|
||||
value = 'one';
|
||||
|
||||
constructor(public vcr: ViewContainerRef) {}
|
||||
|
||||
create() { this.vcr.createEmbeddedView(this.tmp); }
|
||||
create() { return this.vcr.createEmbeddedView(this.tmp, this); }
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: StructuralComp,
|
||||
|
@ -107,32 +116,17 @@ describe('change detection', () => {
|
|||
inputs: {tmp: 'tmp'},
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: (rf: RenderFlags, ctx: StructuralComp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
text(0);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
textBinding(0, bind(ctx.value));
|
||||
}
|
||||
}
|
||||
template: FooTemplate
|
||||
});
|
||||
}
|
||||
|
||||
function FooTemplate(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
text(0, 'Temp content');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <ng-template #foo>
|
||||
* Temp content
|
||||
* </ng-template>
|
||||
* <ng-template #foo>{{ value }}</ng-template>
|
||||
* <structural-comp [tmp]="foo"></structural-comp>
|
||||
*/
|
||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
template(0, FooTemplate, 1, 0, '', null, ['foo', ''], templateRefExtractor);
|
||||
template(0, FooTemplate, 2, 1, '', null, ['foo', ''], templateRefExtractor);
|
||||
element(2, 'structural-comp');
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
|
@ -145,13 +139,19 @@ describe('change detection', () => {
|
|||
fixture.update();
|
||||
expect(fixture.html).toEqual('<structural-comp>one</structural-comp>');
|
||||
|
||||
structuralComp.create();
|
||||
const viewRef: EmbeddedViewRef<any> = structuralComp.create();
|
||||
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';
|
||||
detectChanges(structuralComp);
|
||||
expect(fixture.html).toEqual('<structural-comp>two</structural-comp>Temp content');
|
||||
viewRef.detectChanges();
|
||||
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