Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/src/linker/view_container_ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export abstract class ViewContainerRef {

abstract get injector(): Injector;

/** @deprecated No replacement */
abstract get parentInjector(): Injector;

/**
Expand Down
58 changes: 46 additions & 12 deletions packages/core/src/render3/di.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {InjectionToken} from '../di/injection_token';
import {InjectFlags, Injector, inject, setCurrentInjector} from '../di/injector';
import {InjectFlags, Injector, NullInjector, inject, setCurrentInjector} from '../di/injector';
import * as viewEngine from '../linker';
import {Type} from '../type';

Expand Down Expand Up @@ -552,9 +552,11 @@ export const QUERY_READ_FROM_NODE =
ngDevMode && assertNodeOfPossibleTypes(node, TNodeType.Container, TNodeType.Element);
if (directiveIdx > -1) {
return node.view[DIRECTIVES] ![directiveIdx];
} else if (node.tNode.type === TNodeType.Element) {
}
if (node.tNode.type === TNodeType.Element) {
return getOrCreateElementRef(injector);
} else if (node.tNode.type === TNodeType.Container) {
}
if (node.tNode.type === TNodeType.Container) {
return getOrCreateTemplateRef(injector);
}
throw new Error('fail');
Expand Down Expand Up @@ -600,26 +602,58 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine.ViewContainer

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

di.viewContainerRef = new ViewContainerRef(lContainerNode);
di.viewContainerRef = new ViewContainerRef(lContainerNode, vcRefHost);
}

return di.viewContainerRef;
}

class NodeInjector implements Injector {
constructor(private _lInjector: LInjector) {}

get(token: any): any {
if (token === viewEngine.TemplateRef) {
return getOrCreateTemplateRef(this._lInjector);
}
if (token === viewEngine.ViewContainerRef) {
return getOrCreateContainerRef(this._lInjector);
}
if (token === viewEngine.ElementRef) {
return getOrCreateElementRef(this._lInjector);
}
if (token === viewEngine_ChangeDetectorRef) {
return getOrCreateChangeDetectorRef(this._lInjector, null);
}

return getOrCreateInjectable(this._lInjector, token);
}
}

/**
* A ref to a container that enables adding and removing views from that container
* imperatively.
*/
class ViewContainerRef implements viewEngine.ViewContainerRef {
private _viewRefs: viewEngine.ViewRef[] = [];
// TODO(issue/24571): remove '!'.
element !: viewEngine.ElementRef;
// TODO(issue/24571): remove '!'.
injector !: Injector;
// TODO(issue/24571): remove '!'.
parentInjector !: Injector;

constructor(private _lContainerNode: LContainerNode) {}
constructor(
private _lContainerNode: LContainerNode, private _hostNode: LElementNode|LContainerNode) {}

get element(): ElementRef {
const injector = getOrCreateNodeInjectorForNode(this._hostNode);
return getOrCreateElementRef(injector);
}

get injector(): Injector {
const injector = getOrCreateNodeInjectorForNode(this._hostNode);
return new NodeInjector(injector);
}

/** @deprecated No replacement */
get parentInjector(): Injector {
const parentLInjector = getParentLNode(this._hostNode).nodeInjector;
return parentLInjector ? new NodeInjector(parentLInjector) : new NullInjector();
}

clear(): void {
const lContainer = this._lContainerNode.data;
Expand Down Expand Up @@ -651,7 +685,7 @@ class ViewContainerRef implements viewEngine.ViewContainerRef {
ngModuleRef?: viewEngine.NgModuleRef<any>|undefined): viewEngine.ComponentRef<C> {
const contextInjector = injector || this.parentInjector;
if (!ngModuleRef && contextInjector) {
ngModuleRef = contextInjector.get(viewEngine.NgModuleRef);
ngModuleRef = contextInjector.get(viewEngine.NgModuleRef, null);
}

const componentRef =
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/render3/instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ export function createLViewData<T>(
null, // directives
null, // cleanupInstances
context, // context
viewData && viewData[INJECTOR], // injector
viewData ? viewData[INJECTOR] : null, // injector
renderer, // renderer
sanitizer || null, // sanitizer
null, // tail
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/view/refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class ViewContainerRef_ implements ViewContainerData {

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

/** @deprecated No replacement */
get parentInjector(): Injector {
let view = this._view;
let elDef = this._elDef.parent;
Expand Down
33 changes: 33 additions & 0 deletions packages/core/test/bundling/todo/bundle.golden_symbols.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
{
"name": "ChangeDetectionStrategy"
},
{
"name": "ChangeDetectorRef"
},
{
"name": "DECLARATION_VIEW"
},
Expand All @@ -44,6 +47,9 @@
{
"name": "EMPTY_RENDERER_TYPE_ID"
},
{
"name": "ElementRef"
},
{
"name": "ElementRef$1"
},
Expand Down Expand Up @@ -98,6 +104,12 @@
{
"name": "NgModuleRef"
},
{
"name": "NodeInjector"
},
{
"name": "NullInjector"
},
{
"name": "Optional"
},
Expand Down Expand Up @@ -137,6 +149,9 @@
{
"name": "TVIEW"
},
{
"name": "TemplateRef"
},
{
"name": "TemplateRef$1"
},
Expand Down Expand Up @@ -170,6 +185,9 @@
{
"name": "VIEWS"
},
{
"name": "ViewContainerRef"
},
{
"name": "ViewContainerRef$1"
},
Expand All @@ -191,6 +209,9 @@
{
"name": "_ROOT_DIRECTIVE_INDICES"
},
{
"name": "_THROW_IF_NOT_FOUND"
},
{
"name": "__read"
},
Expand Down Expand Up @@ -494,6 +515,9 @@
{
"name": "getCleanup"
},
{
"name": "getClosestComponentAncestor"
},
{
"name": "getCurrentSanitizer"
},
Expand All @@ -515,12 +539,18 @@
{
"name": "getNextLNode"
},
{
"name": "getOrCreateChangeDetectorRef"
},
{
"name": "getOrCreateContainerRef"
},
{
"name": "getOrCreateElementRef"
},
{
"name": "getOrCreateHostChangeDetector"
},
{
"name": "getOrCreateInjectable"
},
Expand Down Expand Up @@ -617,6 +647,9 @@
{
"name": "invertObject"
},
{
"name": "isComponent"
},
{
"name": "isContextDirty"
},
Expand Down
57 changes: 56 additions & 1 deletion packages/core/test/render3/view_container_ref_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Component, ComponentFactoryResolver, Directive, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
import {Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
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';
Expand Down Expand Up @@ -1035,6 +1035,61 @@ describe('ViewContainerRef', () => {
'<p vcref=""></p><embedded-cmp-with-ngcontent>12<hr>34</embedded-cmp-with-ngcontent>');
});
});

describe('getters', () => {
it('should work on elements', () => {
function createTemplate() {
element(0, 'header', ['vcref', '']);
element(1, 'footer');
}

new TemplateFixture(createTemplate, undefined, [DirectiveWithVCRef]);

expect(directiveInstance !.vcref.element.nativeElement.tagName.toLowerCase())
.toEqual('header');
expect(
directiveInstance !.vcref.injector.get(ElementRef).nativeElement.tagName.toLowerCase())
.toEqual('header');
expect(() => directiveInstance !.vcref.parentInjector.get(ElementRef)).toThrow();
});

it('should work on components', () => {
const HeaderComponent =
createComponent('header-cmp', function(rf: RenderFlags, ctx: any) {});

function createTemplate() {
element(0, 'header-cmp', ['vcref', '']);
element(1, 'footer');
}

new TemplateFixture(createTemplate, undefined, [HeaderComponent, DirectiveWithVCRef]);

expect(directiveInstance !.vcref.element.nativeElement.tagName.toLowerCase())
.toEqual('header-cmp');
expect(
directiveInstance !.vcref.injector.get(ElementRef).nativeElement.tagName.toLowerCase())
.toEqual('header-cmp');
expect(() => directiveInstance !.vcref.parentInjector.get(ElementRef)).toThrow();
});

it('should work on containers', () => {
function createTemplate() {
container(0, embeddedTemplate, undefined, ['vcref', '']);
element(1, 'footer');
}

function updateTemplate() {
containerRefreshStart(0);
containerRefreshEnd();
}

new TemplateFixture(createTemplate, updateTemplate, [DirectiveWithVCRef]);
expect(directiveInstance !.vcref.element.nativeElement.textContent).toEqual('container');
expect(directiveInstance !.vcref.injector.get(ElementRef).nativeElement.textContent)
.toEqual('container');
expect(() => directiveInstance !.vcref.parentInjector.get(ElementRef)).toThrow();
});
});
});

describe('projection', () => {
Expand Down
2 changes: 1 addition & 1 deletion tools/public_api_guard/core/core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ export declare abstract class ViewContainerRef {
abstract readonly element: ElementRef;
abstract readonly injector: Injector;
abstract readonly length: number;
abstract readonly parentInjector: Injector;
/** @deprecated */ abstract readonly parentInjector: Injector;
abstract clear(): void;
abstract createComponent<C>(componentFactory: ComponentFactory<C>, index?: number, injector?: Injector, projectableNodes?: any[][], ngModule?: NgModuleRef<any>): ComponentRef<C>;
abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C>;
Expand Down