Skip to content

Commit cd89eb8

Browse files
marclavalIgorMinar
authored andcommitted
feat(ivy): implement the getters of ViewContainerRef (angular#25174)
BREAKING CHANGE: ViewContainerRef.parentInjector is deprecated without replacement PR Close angular#25174
1 parent e99d860 commit cd89eb8

File tree

7 files changed

+134
-14
lines changed

7 files changed

+134
-14
lines changed

packages/core/src/linker/view_container_ref.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export abstract class ViewContainerRef {
4141

4242
abstract get injector(): Injector;
4343

44+
/** @deprecated No replacement */
4445
abstract get parentInjector(): Injector;
4546

4647
/**

packages/core/src/render3/di.ts

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -552,9 +552,11 @@ export const QUERY_READ_FROM_NODE =
552552
ngDevMode && assertNodeOfPossibleTypes(node, TNodeType.Container, TNodeType.Element);
553553
if (directiveIdx > -1) {
554554
return node.view[DIRECTIVES] ![directiveIdx];
555-
} else if (node.tNode.type === TNodeType.Element) {
555+
}
556+
if (node.tNode.type === TNodeType.Element) {
556557
return getOrCreateElementRef(injector);
557-
} else if (node.tNode.type === TNodeType.Container) {
558+
}
559+
if (node.tNode.type === TNodeType.Container) {
558560
return getOrCreateTemplateRef(injector);
559561
}
560562
throw new Error('fail');
@@ -600,26 +602,55 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine.ViewContainer
600602

601603
addToViewTree(vcRefHost.view, hostTNode.index as number, lContainer);
602604

603-
di.viewContainerRef = new ViewContainerRef(lContainerNode);
605+
di.viewContainerRef = new ViewContainerRef(lContainerNode, vcRefHost);
604606
}
605607

606608
return di.viewContainerRef;
607609
}
608610

611+
class NodeInjector implements Injector {
612+
constructor(private _lInjector: LInjector) {}
613+
614+
get(token: any): any {
615+
if (token === viewEngine.TemplateRef) {
616+
return getOrCreateTemplateRef(this._lInjector);
617+
}
618+
if (token === viewEngine.ViewContainerRef) {
619+
return getOrCreateContainerRef(this._lInjector);
620+
}
621+
if (token === viewEngine.ElementRef) {
622+
return getOrCreateElementRef(this._lInjector);
623+
}
624+
if (token === viewEngine_ChangeDetectorRef) {
625+
return getOrCreateChangeDetectorRef(this._lInjector, null);
626+
}
627+
628+
return getOrCreateInjectable(this._lInjector, token);
629+
}
630+
}
631+
609632
/**
610633
* A ref to a container that enables adding and removing views from that container
611634
* imperatively.
612635
*/
613636
class ViewContainerRef implements viewEngine.ViewContainerRef {
614637
private _viewRefs: viewEngine.ViewRef[] = [];
615-
// TODO(issue/24571): remove '!'.
616-
element !: viewEngine.ElementRef;
617-
// TODO(issue/24571): remove '!'.
618-
injector !: Injector;
619-
// TODO(issue/24571): remove '!'.
620-
parentInjector !: Injector;
621638

622-
constructor(private _lContainerNode: LContainerNode) {}
639+
640+
constructor(
641+
private _lContainerNode: LContainerNode, private _hostNode: LElementNode|LContainerNode) {}
642+
643+
get element(): ElementRef { return new ElementRef(this._hostNode.native); }
644+
645+
get injector(): Injector {
646+
return new NodeInjector(getOrCreateNodeInjectorForNode(this._hostNode));
647+
}
648+
649+
/** @deprecated No replacement */
650+
get parentInjector(): Injector {
651+
const parentLInjector = getParentLNode(this._hostNode).nodeInjector;
652+
return parentLInjector ? new NodeInjector(parentLInjector) : Injector.NULL;
653+
}
623654

624655
clear(): void {
625656
const lContainer = this._lContainerNode.data;
@@ -651,7 +682,7 @@ class ViewContainerRef implements viewEngine.ViewContainerRef {
651682
ngModuleRef?: viewEngine.NgModuleRef<any>|undefined): viewEngine.ComponentRef<C> {
652683
const contextInjector = injector || this.parentInjector;
653684
if (!ngModuleRef && contextInjector) {
654-
ngModuleRef = contextInjector.get(viewEngine.NgModuleRef);
685+
ngModuleRef = contextInjector.get(viewEngine.NgModuleRef, null);
655686
}
656687

657688
const componentRef =

packages/core/src/render3/instructions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ export function createLViewData<T>(
336336
null, // directives
337337
null, // cleanupInstances
338338
context, // context
339-
viewData && viewData[INJECTOR], // injector
339+
viewData && viewData[INJECTOR] || null, // injector
340340
renderer, // renderer
341341
sanitizer || null, // sanitizer
342342
null, // tail

packages/core/src/view/refs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class ViewContainerRef_ implements ViewContainerData {
138138

139139
get injector(): Injector { return new Injector_(this._view, this._elDef); }
140140

141+
/** @deprecated No replacement */
141142
get parentInjector(): Injector {
142143
let view = this._view;
143144
let elDef = this._elDef.parent;

packages/core/test/bundling/todo/bundle.golden_symbols.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
{
4545
"name": "EMPTY_RENDERER_TYPE_ID"
4646
},
47+
{
48+
"name": "ElementRef"
49+
},
4750
{
4851
"name": "ElementRef$1"
4952
},
@@ -98,6 +101,12 @@
98101
{
99102
"name": "NgModuleRef"
100103
},
104+
{
105+
"name": "NodeInjector"
106+
},
107+
{
108+
"name": "NullInjector"
109+
},
101110
{
102111
"name": "Optional"
103112
},
@@ -173,6 +182,9 @@
173182
{
174183
"name": "ViewContainerRef$1"
175184
},
185+
{
186+
"name": "ViewContainerRef$1"
187+
},
176188
{
177189
"name": "ViewEncapsulation$1"
178190
},
@@ -191,6 +203,9 @@
191203
{
192204
"name": "_ROOT_DIRECTIVE_INDICES"
193205
},
206+
{
207+
"name": "_THROW_IF_NOT_FOUND"
208+
},
194209
{
195210
"name": "__read"
196211
},
@@ -494,6 +509,9 @@
494509
{
495510
"name": "getCleanup"
496511
},
512+
{
513+
"name": "getClosestComponentAncestor"
514+
},
497515
{
498516
"name": "getCurrentSanitizer"
499517
},
@@ -515,12 +533,18 @@
515533
{
516534
"name": "getNextLNode"
517535
},
536+
{
537+
"name": "getOrCreateChangeDetectorRef"
538+
},
518539
{
519540
"name": "getOrCreateContainerRef"
520541
},
521542
{
522543
"name": "getOrCreateElementRef"
523544
},
545+
{
546+
"name": "getOrCreateHostChangeDetector"
547+
},
524548
{
525549
"name": "getOrCreateInjectable"
526550
},
@@ -617,6 +641,9 @@
617641
{
618642
"name": "invertObject"
619643
},
644+
{
645+
"name": "isComponent"
646+
},
620647
{
621648
"name": "isContextDirty"
622649
},

packages/core/test/render3/view_container_ref_spec.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Component, ComponentFactoryResolver, Directive, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
9+
import {Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
1010
import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
1111
import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
1212
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, load, loadDirective, nextContext, projection, projectionDef, reserveSlots, text, textBinding} from '../../src/render3/instructions';
@@ -1035,6 +1035,66 @@ describe('ViewContainerRef', () => {
10351035
'<p vcref=""></p><embedded-cmp-with-ngcontent>12<hr>34</embedded-cmp-with-ngcontent>');
10361036
});
10371037
});
1038+
1039+
describe('getters', () => {
1040+
it('should work on elements', () => {
1041+
function createTemplate() {
1042+
elementStart(0, 'header', ['vcref', '']);
1043+
elementEnd();
1044+
elementStart(1, 'footer');
1045+
elementEnd();
1046+
}
1047+
1048+
new TemplateFixture(createTemplate, undefined, [DirectiveWithVCRef]);
1049+
1050+
expect(directiveInstance !.vcref.element.nativeElement.tagName.toLowerCase())
1051+
.toEqual('header');
1052+
expect(
1053+
directiveInstance !.vcref.injector.get(ElementRef).nativeElement.tagName.toLowerCase())
1054+
.toEqual('header');
1055+
expect(() => directiveInstance !.vcref.parentInjector.get(ElementRef)).toThrow();
1056+
});
1057+
1058+
it('should work on components', () => {
1059+
const HeaderComponent =
1060+
createComponent('header-cmp', function(rf: RenderFlags, ctx: any) {});
1061+
1062+
function createTemplate() {
1063+
elementStart(0, 'header-cmp', ['vcref', '']);
1064+
elementEnd();
1065+
elementStart(1, 'footer');
1066+
elementEnd();
1067+
}
1068+
1069+
new TemplateFixture(createTemplate, undefined, [HeaderComponent, DirectiveWithVCRef]);
1070+
1071+
expect(directiveInstance !.vcref.element.nativeElement.tagName.toLowerCase())
1072+
.toEqual('header-cmp');
1073+
expect(
1074+
directiveInstance !.vcref.injector.get(ElementRef).nativeElement.tagName.toLowerCase())
1075+
.toEqual('header-cmp');
1076+
expect(() => directiveInstance !.vcref.parentInjector.get(ElementRef)).toThrow();
1077+
});
1078+
1079+
it('should work on containers', () => {
1080+
function createTemplate() {
1081+
container(0, embeddedTemplate, undefined, ['vcref', '']);
1082+
elementStart(1, 'footer');
1083+
elementEnd();
1084+
}
1085+
1086+
function updateTemplate() {
1087+
containerRefreshStart(0);
1088+
containerRefreshEnd();
1089+
}
1090+
1091+
new TemplateFixture(createTemplate, updateTemplate, [DirectiveWithVCRef]);
1092+
expect(directiveInstance !.vcref.element.nativeElement.textContent).toEqual('container');
1093+
expect(directiveInstance !.vcref.injector.get(ElementRef).nativeElement.textContent)
1094+
.toEqual('container');
1095+
expect(() => directiveInstance !.vcref.parentInjector.get(ElementRef)).toThrow();
1096+
});
1097+
});
10381098
});
10391099

10401100
describe('projection', () => {

tools/public_api_guard/core/core.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,7 @@ export declare abstract class ViewContainerRef {
922922
abstract readonly element: ElementRef;
923923
abstract readonly injector: Injector;
924924
abstract readonly length: number;
925-
abstract readonly parentInjector: Injector;
925+
/** @deprecated */ abstract readonly parentInjector: Injector;
926926
abstract clear(): void;
927927
abstract createComponent<C>(componentFactory: ComponentFactory<C>, index?: number, injector?: Injector, projectableNodes?: any[][], ngModule?: NgModuleRef<any>): ComponentRef<C>;
928928
abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C>;

0 commit comments

Comments
 (0)