parent
8fdb1e09c1
commit
5bc869cb24
|
@ -6,12 +6,23 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ComponentFactory, ComponentRef as IComponentRef, ElementRef as IElementRef, EmbeddedViewRef as IEmbeddedViewRef, Injector, NgModuleRef as INgModuleRef, TemplateRef as ITemplateRef, Type, ViewContainerRef as IViewContainerRef, ViewRef as IViewRef} from '../core';
|
// We are temporarily importing the existing viewEngine from core so we can be sure we are
|
||||||
|
// correctly implementing its interfaces for backwards compatibility.
|
||||||
|
import * as viewEngine from '../core';
|
||||||
import {BLOOM_SIZE, NG_ELEMENT_ID, getOrCreateNodeInjector} from './instructions';
|
import {BLOOM_SIZE, NG_ELEMENT_ID, getOrCreateNodeInjector} from './instructions';
|
||||||
import {LContainer, LNodeFlags, LNodeInjector} from './interfaces';
|
import {LContainer, LNodeFlags, LNodeInjector} from './interfaces';
|
||||||
import {ComponentTemplate, DirectiveDef} from './public_interfaces';
|
import {ComponentTemplate, DirectiveDef} from './public_interfaces';
|
||||||
import {stringify} from './util';
|
import {stringify, notImplemented} from './util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection flags for DI.
|
||||||
|
*
|
||||||
|
* Optional: The dependency is not required.
|
||||||
|
*
|
||||||
|
* CheckSelf: Should check the current node for the dependency.
|
||||||
|
*
|
||||||
|
* CheckParent: Should check parent nodes for the dependency.
|
||||||
|
*/
|
||||||
export const enum InjectFlags {
|
export const enum InjectFlags {
|
||||||
Optional = 1 << 0,
|
Optional = 1 << 0,
|
||||||
CheckSelf = 1 << 1,
|
CheckSelf = 1 << 1,
|
||||||
|
@ -19,17 +30,40 @@ export const enum InjectFlags {
|
||||||
Default = CheckSelf | CheckParent
|
Default = CheckSelf | CheckParent
|
||||||
}
|
}
|
||||||
|
|
||||||
function createError(text: string, token: any) {
|
/**
|
||||||
|
* Constructs an injection error with the given text and token.
|
||||||
|
*
|
||||||
|
* @param text The text of the error
|
||||||
|
* @param token The token associated with the error
|
||||||
|
* @returns The error that was created
|
||||||
|
*/
|
||||||
|
function createInjectionError(text: string, token: any) {
|
||||||
return new Error(`ElementInjector: ${text} [${stringify(token)}]`);
|
return new Error(`ElementInjector: ${text} [${stringify(token)}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function inject<T>(token: Type<T>, flags?: InjectFlags): T {
|
/**
|
||||||
|
* Searches for an instance of the given directive type up the injector tree and returns
|
||||||
|
* that instance if found.
|
||||||
|
*
|
||||||
|
* Specifically, it gets the bloom filter bit associated with the directive (see bloomHashBit),
|
||||||
|
* checks that bit against the bloom filter structure to identify an injector that might have
|
||||||
|
* the directive (see bloomFindPossibleInjector), then searches the directives on that injector
|
||||||
|
* for a match.
|
||||||
|
*
|
||||||
|
* If not found, it will propagate up to the next parent injector until the token
|
||||||
|
* is found or the top is reached.
|
||||||
|
*
|
||||||
|
* @param token The directive type to search for
|
||||||
|
* @param flags Injection flags (e.g. CheckParent)
|
||||||
|
* @returns The instance found
|
||||||
|
*/
|
||||||
|
export function inject<T>(token: viewEngine.Type<T>, flags?: InjectFlags): T {
|
||||||
const di = getOrCreateNodeInjector();
|
const di = getOrCreateNodeInjector();
|
||||||
const bloomHash = bloomHashBit(token);
|
const bloomHash = bloomHashBit(token);
|
||||||
if (bloomHash === null) {
|
if (bloomHash === null) {
|
||||||
const moduleInjector = di.injector;
|
const moduleInjector = di.injector;
|
||||||
if (!moduleInjector) {
|
if (!moduleInjector) {
|
||||||
throw createError('NotFound', token);
|
throw createInjectionError('NotFound', token);
|
||||||
}
|
}
|
||||||
moduleInjector.get(token);
|
moduleInjector.get(token);
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,14 +89,45 @@ export function inject<T>(token: Type<T>, flags?: InjectFlags): T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw createError('Not found', token);
|
throw createInjectionError('Not found', token);
|
||||||
}
|
}
|
||||||
|
|
||||||
function bloomHashBit(type: Type<any>): number|null {
|
/**
|
||||||
|
* Given a directive type, this function returns the bit in an injector's bloom filter
|
||||||
|
* that should be used to determine whether or not the directive is present.
|
||||||
|
*
|
||||||
|
* When the directive was added to the bloom filter, it was given a unique ID that can be
|
||||||
|
* retrieved on the class. Since there are only BLOOM_SIZE slots per bloom filter, the directive's
|
||||||
|
* ID must be modulo-ed by BLOOM_SIZE to get the correct bloom bit (directives share slots after
|
||||||
|
* BLOOM_SIZE is reached).
|
||||||
|
*
|
||||||
|
* @param type The directive type
|
||||||
|
* @returns The bloom bit to check for the directive
|
||||||
|
*/
|
||||||
|
function bloomHashBit(type: viewEngine.Type<any>): number|null {
|
||||||
let id: number|undefined = (type as any)[NG_ELEMENT_ID];
|
let id: number|undefined = (type as any)[NG_ELEMENT_ID];
|
||||||
return typeof id === 'number' ? id % BLOOM_SIZE : null;
|
return typeof id === 'number' ? id % BLOOM_SIZE : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the closest injector that might have a certain directive.
|
||||||
|
*
|
||||||
|
* Each directive corresponds to a bit in an injector's bloom filter. Given the bloom bit to
|
||||||
|
* check and a starting injector, this function propagates up injectors until it finds an
|
||||||
|
* injector that contains a 1 for that bit in its bloom filter. A 1 indicates that the
|
||||||
|
* injector may have that directive. It only *may* have the directive because directives begin
|
||||||
|
* to share bloom filter bits after the BLOOM_SIZE is reached, and it could correspond to a
|
||||||
|
* different directive sharing the bit.
|
||||||
|
*
|
||||||
|
* Note: We can skip checking further injectors up the tree if an injector's cbf structure
|
||||||
|
* has a 0 for that bloom bit. Since cbf contains the merged value of all the parent
|
||||||
|
* injectors, a 0 in the bloom bit indicates that the parents definitely do not contain
|
||||||
|
* the directive and do not need to be checked.
|
||||||
|
*
|
||||||
|
* @param injector The starting injector to check
|
||||||
|
* @param bloomBit The bit to check in each injector's bloom filter
|
||||||
|
* @returns An injector that might have the directive
|
||||||
|
*/
|
||||||
export function bloomFindPossibleInjector(injector: LNodeInjector, bloomBit: number): LNodeInjector|
|
export function bloomFindPossibleInjector(injector: LNodeInjector, bloomBit: number): LNodeInjector|
|
||||||
null {
|
null {
|
||||||
const mask = 1 << bloomBit;
|
const mask = 1 << bloomBit;
|
||||||
|
@ -83,72 +148,89 @@ export function bloomFindPossibleInjector(injector: LNodeInjector, bloomBit: num
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
export function injectElementRef(): IElementRef {
|
* Creates an ElementRef and stores it on the injector. Or, if the ElementRef already
|
||||||
|
* exists, retrieves the existing ElementRef.
|
||||||
|
*
|
||||||
|
* @returns The ElementRef instance to use
|
||||||
|
*/
|
||||||
|
export function injectElementRef(): viewEngine.ElementRef {
|
||||||
let di = getOrCreateNodeInjector();
|
let di = getOrCreateNodeInjector();
|
||||||
return di.elementRef || (di.elementRef = new ElementRef(di.node.native));
|
return di.elementRef || (di.elementRef = new ElementRef(di.node.native));
|
||||||
}
|
}
|
||||||
|
|
||||||
class ElementRef implements IElementRef {
|
/** A ref to a node's native element. */
|
||||||
|
class ElementRef implements viewEngine.ElementRef {
|
||||||
readonly nativeElement: any;
|
readonly nativeElement: any;
|
||||||
constructor(nativeElement: any) { this.nativeElement = nativeElement; }
|
constructor(nativeElement: any) { this.nativeElement = nativeElement; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
export function injectTemplateRef(): ITemplateRef<any> {
|
* Creates a TemplateRef and stores it on the injector. Or, if the TemplateRef already
|
||||||
|
* exists, retrieves the existing TemplateRef.
|
||||||
|
*
|
||||||
|
* @returns The TemplateRef instance to use
|
||||||
|
*/
|
||||||
|
export function injectTemplateRef(): viewEngine.TemplateRef<any> {
|
||||||
let di = getOrCreateNodeInjector();
|
let di = getOrCreateNodeInjector();
|
||||||
const data = (di.node as LContainer).data;
|
const data = (di.node as LContainer).data;
|
||||||
if (data === null || data.template === null) {
|
if (data === null || data.template === null) {
|
||||||
throw createError('Directive not used in structural way.', null);
|
throw createInjectionError('Directive does not have a template.', null);
|
||||||
}
|
}
|
||||||
return di.templateRef ||
|
return di.templateRef ||
|
||||||
(di.templateRef = new TemplateRef<any>(injectElementRef(), data.template));
|
(di.templateRef = new TemplateRef<any>(injectElementRef(), data.template));
|
||||||
}
|
}
|
||||||
|
|
||||||
class TemplateRef<T> implements ITemplateRef<T> {
|
/** A ref to a particular template. */
|
||||||
readonly elementRef: IElementRef;
|
class TemplateRef<T> implements viewEngine.TemplateRef<T> {
|
||||||
|
readonly elementRef: viewEngine.ElementRef;
|
||||||
|
|
||||||
constructor(elementRef: IElementRef, template: ComponentTemplate<T>) {
|
constructor(elementRef: viewEngine.ElementRef, template: ComponentTemplate<T>) {
|
||||||
this.elementRef = elementRef;
|
this.elementRef = elementRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
createEmbeddedView(context: T): IEmbeddedViewRef<T> { throw notImplemented(); }
|
createEmbeddedView(context: T): viewEngine.EmbeddedViewRef<T> { throw notImplemented(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function injectViewContainerRef(): IViewContainerRef {
|
/**
|
||||||
|
* Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef
|
||||||
|
* already exists, retrieves the existing ViewContainerRef.
|
||||||
|
*
|
||||||
|
* @returns The ViewContainerRef instance to use
|
||||||
|
*/
|
||||||
|
export function injectViewContainerRef(): viewEngine.ViewContainerRef {
|
||||||
let di = getOrCreateNodeInjector();
|
let di = getOrCreateNodeInjector();
|
||||||
return di.viewContainerRef || (di.viewContainerRef = new ViewContainerRef(di.node as LContainer));
|
return di.viewContainerRef || (di.viewContainerRef = new ViewContainerRef(di.node as LContainer));
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewContainerRef implements IViewContainerRef {
|
/**
|
||||||
element: IElementRef;
|
* A ref to a container that enables adding and removing views from that container
|
||||||
injector: Injector;
|
* imperatively.
|
||||||
parentInjector: Injector;
|
*/
|
||||||
|
class ViewContainerRef implements viewEngine.ViewContainerRef {
|
||||||
|
element: viewEngine.ElementRef;
|
||||||
|
injector: viewEngine.Injector;
|
||||||
|
parentInjector: viewEngine.Injector;
|
||||||
|
|
||||||
constructor(node: LContainer) {}
|
constructor(node: LContainer) {}
|
||||||
|
|
||||||
clear(): void { throw notImplemented(); }
|
clear(): void { throw notImplemented(); }
|
||||||
get(index: number): IViewRef|null { throw notImplemented(); }
|
get(index: number): viewEngine.ViewRef|null { throw notImplemented(); }
|
||||||
length: number;
|
length: number;
|
||||||
createEmbeddedView<C>(
|
createEmbeddedView<C>(
|
||||||
templateRef: ITemplateRef<C>, context?: C|undefined,
|
templateRef: viewEngine.TemplateRef<C>, context?: C|undefined,
|
||||||
index?: number|undefined): IEmbeddedViewRef<C> {
|
index?: number|undefined): viewEngine.EmbeddedViewRef<C> {
|
||||||
throw notImplemented();
|
throw notImplemented();
|
||||||
}
|
}
|
||||||
createComponent<C>(
|
createComponent<C>(
|
||||||
componentFactory: ComponentFactory<C>, index?: number|undefined,
|
componentFactory: viewEngine.ComponentFactory<C>, index?: number|undefined,
|
||||||
injector?: Injector|undefined, projectableNodes?: any[][]|undefined,
|
injector?: viewEngine.Injector|undefined, projectableNodes?: any[][]|undefined,
|
||||||
ngModule?: INgModuleRef<any>|undefined): IComponentRef<C> {
|
ngModule?: viewEngine.NgModuleRef<any>|undefined): viewEngine.ComponentRef<C> {
|
||||||
throw notImplemented();
|
throw notImplemented();
|
||||||
}
|
}
|
||||||
insert(viewRef: IViewRef, index?: number|undefined): IViewRef { throw notImplemented(); }
|
insert(viewRef: viewEngine.ViewRef, index?: number|undefined): viewEngine.ViewRef { throw notImplemented(); }
|
||||||
move(viewRef: IViewRef, currentIndex: number): IViewRef { throw notImplemented(); }
|
move(viewRef: viewEngine.ViewRef, currentIndex: number): viewEngine.ViewRef { throw notImplemented(); }
|
||||||
indexOf(viewRef: IViewRef): number { throw notImplemented(); }
|
indexOf(viewRef: viewEngine.ViewRef): number { throw notImplemented(); }
|
||||||
remove(index?: number|undefined): void { throw notImplemented(); }
|
remove(index?: number|undefined): void { throw notImplemented(); }
|
||||||
detach(index?: number|undefined): IViewRef|null { throw notImplemented(); }
|
detach(index?: number|undefined): viewEngine.ViewRef|null { throw notImplemented(); }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function notImplemented() {
|
|
||||||
return new Error('Method not implemented.');
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,4 +20,14 @@ export function stringify(value: any): string {
|
||||||
if (typeof value == 'string') return value;
|
if (typeof value == 'string') return value;
|
||||||
if (value == null) return '';
|
if (value == null) return '';
|
||||||
return '' + value;
|
return '' + value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that throws a "not implemented" error so it's clear certain
|
||||||
|
* behaviors/methods aren't yet ready.
|
||||||
|
*
|
||||||
|
* @returns Not implemented error
|
||||||
|
*/
|
||||||
|
export function notImplemented(): Error {
|
||||||
|
return new Error('NotImplemented');
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue