diff --git a/goldens/public-api/core/core.d.ts b/goldens/public-api/core/core.d.ts index 56dfa25b8c..a9f381b639 100644 --- a/goldens/public-api/core/core.d.ts +++ b/goldens/public-api/core/core.d.ts @@ -301,7 +301,7 @@ export declare class ElementRef { } export declare abstract class EmbeddedViewRef extends ViewRef { - abstract get context(): C; + abstract context: C; abstract get rootNodes(): any[]; } diff --git a/packages/core/src/linker/view_ref.ts b/packages/core/src/linker/view_ref.ts index bafc35a488..315f86e6d7 100644 --- a/packages/core/src/linker/view_ref.ts +++ b/packages/core/src/linker/view_ref.ts @@ -93,7 +93,7 @@ export abstract class EmbeddedViewRef extends ViewRef { /** * The context for this view, inherited from the anchor element. */ - abstract get context(): C; + abstract context: C; /** * The root nodes for this embedded view. diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index b88e8b51ba..8f7fdc72fa 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -61,6 +61,10 @@ export class ViewRef implements viewEngine_EmbeddedViewRef, viewEngine_Int return this._lView[CONTEXT] as T; } + set context(value: T) { + this._lView[CONTEXT] = value; + } + get destroyed(): boolean { return (this._lView[FLAGS] & LViewFlags.Destroyed) === LViewFlags.Destroyed; } diff --git a/packages/core/src/view/refs.ts b/packages/core/src/view/refs.ts index 7d219ace1c..b724fcc800 100644 --- a/packages/core/src/view/refs.ts +++ b/packages/core/src/view/refs.ts @@ -264,6 +264,10 @@ export class ViewRef_ implements EmbeddedViewRef, InternalViewRef { return this._view.context; } + set context(value: any) { + this._view.context = value; + } + get destroyed(): boolean { return (this._view.state & ViewState.Destroyed) !== 0; } diff --git a/packages/core/test/acceptance/template_ref_spec.ts b/packages/core/test/acceptance/template_ref_spec.ts index ab14c0c765..84ed32d582 100644 --- a/packages/core/test/acceptance/template_ref_spec.ts +++ b/packages/core/test/acceptance/template_ref_spec.ts @@ -273,4 +273,87 @@ describe('TemplateRef', () => { }); }); }); + + describe('context', () => { + @Component({ + template: ` + {{name}} + + ` + }) + class App { + @ViewChild('templateRef') templateRef!: TemplateRef; + @ViewChild('containerRef', {read: ViewContainerRef}) containerRef!: ViewContainerRef; + } + + it('should update if the context of a view ref is mutated', () => { + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const context = {name: 'Frodo'}; + const viewRef = fixture.componentInstance.templateRef.createEmbeddedView(context); + fixture.componentInstance.containerRef.insert(viewRef); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Frodo'); + + context.name = 'Bilbo'; + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Bilbo'); + }); + + it('should update if the context of a view ref is replaced', () => { + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const viewRef = fixture.componentInstance.templateRef.createEmbeddedView({name: 'Frodo'}); + fixture.componentInstance.containerRef.insert(viewRef); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Frodo'); + + viewRef.context = {name: 'Bilbo'}; + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Bilbo'); + }); + + it('should use the latest context information inside template listeners', () => { + const events: string[] = []; + + @Component({ + template: ` + + + + + ` + }) + class ListenerTest { + @ViewChild('templateRef') templateRef!: TemplateRef; + @ViewChild('containerRef', {read: ViewContainerRef}) containerRef!: ViewContainerRef; + + log(name: string) { + events.push(name); + } + } + + TestBed.configureTestingModule({declarations: [ListenerTest]}); + const fixture = TestBed.createComponent(ListenerTest); + fixture.detectChanges(); + const viewRef = fixture.componentInstance.templateRef.createEmbeddedView({name: 'Frodo'}); + fixture.componentInstance.containerRef.insert(viewRef); + fixture.detectChanges(); + + const button = fixture.nativeElement.querySelector('button'); + button.click(); + expect(events).toEqual(['Frodo']); + + viewRef.context = {name: 'Bilbo'}; + fixture.detectChanges(); + button.click(); + expect(events).toEqual(['Frodo', 'Bilbo']); + }); + }); });