Skip to content

Commit

Permalink
fix(core): allow EmbeddedViewRef context to be updated
Browse files Browse the repository at this point in the history
Currently `EmbeddedViewRef.context` is read-only which means that the only way to update
it is to mutate the object which can lead to some undesirable outcomes if the template
and the context are provided by an external consumer (see angular#24515).

These changes make the property writeable since there doesn't appear to be a specific
reason why it was readonly to begin with.
  • Loading branch information
crisbeto committed Jan 8, 2021
1 parent da6c739 commit 66b3378
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 2 deletions.
2 changes: 1 addition & 1 deletion goldens/public-api/core/core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ export declare class ElementRef<T = any> {
}

export declare abstract class EmbeddedViewRef<C> extends ViewRef {
abstract get context(): C;
abstract context: C;
abstract get rootNodes(): any[];
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/linker/view_ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export abstract class EmbeddedViewRef<C> 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.
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/render3/view_ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, 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;
}
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/view/refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ export class ViewRef_ implements EmbeddedViewRef<any>, InternalViewRef {
return this._view.context;
}

set context(value: any) {
this._view.context = value;
}

get destroyed(): boolean {
return (this._view.state & ViewState.Destroyed) !== 0;
}
Expand Down
46 changes: 46 additions & 0 deletions packages/core/test/acceptance/template_ref_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,50 @@ describe('TemplateRef', () => {
});
});
});

describe('context', () => {
@Component({
template: `
<ng-template #templateRef let-name="name">{{name}}</ng-template>
<ng-container #containerRef></ng-container>
`
})
class App {
@ViewChild('templateRef') templateRef!: TemplateRef<any>;
@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');
});
});
});

0 comments on commit 66b3378

Please sign in to comment.