parent
ab5bc42da0
commit
db77d8dc92
|
@ -413,19 +413,19 @@ function getClosureSafeProperty<T>(objWithPropertyToExtract: T): string {
|
||||||
* Injection flags for DI.
|
* Injection flags for DI.
|
||||||
*/
|
*/
|
||||||
export const enum InjectFlags {
|
export const enum InjectFlags {
|
||||||
Default = 0,
|
Default = 0b0000,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies that an injector should retrieve a dependency from any injector until reaching the
|
* Specifies that an injector should retrieve a dependency from any injector until reaching the
|
||||||
* host element of the current component. (Only used with Element Injector)
|
* host element of the current component. (Only used with Element Injector)
|
||||||
*/
|
*/
|
||||||
Host = 1 << 0,
|
Host = 0b0001,
|
||||||
/** Don't descend into ancestors of the node requesting injection. */
|
/** Don't descend into ancestors of the node requesting injection. */
|
||||||
Self = 1 << 1,
|
Self = 0b0010,
|
||||||
/** Skip the node that is requesting injection. */
|
/** Skip the node that is requesting injection. */
|
||||||
SkipSelf = 1 << 2,
|
SkipSelf = 0b0100,
|
||||||
/** Inject `defaultValue` instead if token not found. */
|
/** Inject `defaultValue` instead if token not found. */
|
||||||
Optional = 1 << 3,
|
Optional = 0b1000,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -467,6 +467,7 @@ export function inject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags
|
||||||
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
|
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
|
||||||
injectableDef.value;
|
injectableDef.value;
|
||||||
}
|
}
|
||||||
|
if (flags & InjectFlags.Optional) return null;
|
||||||
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
|
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
|
||||||
} else {
|
} else {
|
||||||
return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
|
return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
|
||||||
|
|
|
@ -178,7 +178,8 @@ export function diPublic(def: DirectiveDef<any>): void {
|
||||||
* @returns The instance found
|
* @returns The instance found
|
||||||
*/
|
*/
|
||||||
export function directiveInject<T>(token: Type<T>): T;
|
export function directiveInject<T>(token: Type<T>): T;
|
||||||
export function directiveInject<T>(token: Type<T>, flags?: InjectFlags): T|null;
|
export function directiveInject<T>(token: Type<T>, flags: InjectFlags.Optional): T|null;
|
||||||
|
export function directiveInject<T>(token: Type<T>, flags: InjectFlags): T;
|
||||||
export function directiveInject<T>(token: Type<T>, flags = InjectFlags.Default): T|null {
|
export function directiveInject<T>(token: Type<T>, flags = InjectFlags.Default): T|null {
|
||||||
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), token, flags);
|
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), token, flags);
|
||||||
}
|
}
|
||||||
|
@ -329,8 +330,8 @@ function getClosestComponentAncestor(node: LViewNode | LElementNode): LElementNo
|
||||||
* @param flags Injection flags (e.g. CheckParent)
|
* @param flags Injection flags (e.g. CheckParent)
|
||||||
* @returns The instance found
|
* @returns The instance found
|
||||||
*/
|
*/
|
||||||
export function getOrCreateInjectable<T>(di: LInjector, token: Type<T>, flags?: InjectFlags): T|
|
export function getOrCreateInjectable<T>(
|
||||||
null {
|
di: LInjector, token: Type<T>, flags: InjectFlags = InjectFlags.Default): T|null {
|
||||||
const bloomHash = bloomHashBit(token);
|
const bloomHash = bloomHashBit(token);
|
||||||
|
|
||||||
// If the token has a bloom hash, then it is a directive that is public to the injection system
|
// If the token has a bloom hash, then it is a directive that is public to the injection system
|
||||||
|
@ -349,7 +350,7 @@ export function getOrCreateInjectable<T>(di: LInjector, token: Type<T>, flags?:
|
||||||
while (injector) {
|
while (injector) {
|
||||||
// Get the closest potential matching injector (upwards in the injector tree) that
|
// Get the closest potential matching injector (upwards in the injector tree) that
|
||||||
// *potentially* has the token.
|
// *potentially* has the token.
|
||||||
injector = bloomFindPossibleInjector(injector, bloomHash);
|
injector = bloomFindPossibleInjector(injector, bloomHash, flags);
|
||||||
|
|
||||||
// If no injector is found, we *know* that there is no ancestor injector that contains the
|
// If no injector is found, we *know* that there is no ancestor injector that contains the
|
||||||
// token, so we abort.
|
// token, so we abort.
|
||||||
|
@ -360,11 +361,11 @@ export function getOrCreateInjectable<T>(di: LInjector, token: Type<T>, flags?:
|
||||||
// At this point, we have an injector which *may* contain the token, so we step through the
|
// At this point, we have an injector which *may* contain the token, so we step through the
|
||||||
// directives associated with the injector's corresponding node to get the directive instance.
|
// directives associated with the injector's corresponding node to get the directive instance.
|
||||||
const node = injector.node;
|
const node = injector.node;
|
||||||
const flags = node.tNode !.flags;
|
const nodeFlags = node.tNode !.flags;
|
||||||
const count = flags & TNodeFlags.DirectiveCountMask;
|
const count = nodeFlags & TNodeFlags.DirectiveCountMask;
|
||||||
|
|
||||||
if (count !== 0) {
|
if (count !== 0) {
|
||||||
const start = flags >> TNodeFlags.DirectiveStartingIndexShift;
|
const start = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||||
const end = start + count;
|
const end = start + count;
|
||||||
const defs = node.view.tView.directives !;
|
const defs = node.view.tView.directives !;
|
||||||
|
|
||||||
|
@ -385,15 +386,19 @@ export function getOrCreateInjectable<T>(di: LInjector, token: Type<T>, flags?:
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The def wasn't found anywhere on this node, so it might be a false positive.
|
// The def wasn't found anywhere on this node, so it was a false positive.
|
||||||
// Traverse up the tree and continue searching.
|
// If flags permit, traverse up the tree and continue searching.
|
||||||
injector = injector.parent;
|
if (flags & InjectFlags.Self || flags & InjectFlags.Host && !sameHostView(injector)) {
|
||||||
|
injector = null;
|
||||||
|
} else {
|
||||||
|
injector = injector.parent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No directive was found for the given token.
|
// No directive was found for the given token.
|
||||||
// TODO: implement optional, check-self, and check-parent.
|
if (flags & InjectFlags.Optional) return null;
|
||||||
throw new Error('Implement');
|
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchMatchesQueuedForCreation<T>(node: LNode, token: any): T|null {
|
function searchMatchesQueuedForCreation<T>(node: LNode, token: any): T|null {
|
||||||
|
@ -443,10 +448,11 @@ function bloomHashBit(type: Type<any>): number|null {
|
||||||
*
|
*
|
||||||
* @param injector The starting node injector to check
|
* @param injector The starting node injector to check
|
||||||
* @param bloomBit The bit to check in each injector's bloom filter
|
* @param bloomBit The bit to check in each injector's bloom filter
|
||||||
|
* @param flags The injection flags for this injection site (e.g. Optional or SkipSelf)
|
||||||
* @returns An injector that might have the directive
|
* @returns An injector that might have the directive
|
||||||
*/
|
*/
|
||||||
export function bloomFindPossibleInjector(startInjector: LInjector, bloomBit: number): LInjector|
|
export function bloomFindPossibleInjector(
|
||||||
null {
|
startInjector: LInjector, bloomBit: number, flags: InjectFlags): LInjector|null {
|
||||||
// Create a mask that targets the specific bit associated with the directive we're looking for.
|
// Create a mask that targets the specific bit associated with the directive we're looking for.
|
||||||
// JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
|
// JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
|
||||||
// to bit positions 0 - 31 in a 32 bit integer.
|
// to bit positions 0 - 31 in a 32 bit integer.
|
||||||
|
@ -454,7 +460,8 @@ export function bloomFindPossibleInjector(startInjector: LInjector, bloomBit: nu
|
||||||
|
|
||||||
// Traverse up the injector tree until we find a potential match or until we know there *isn't* a
|
// Traverse up the injector tree until we find a potential match or until we know there *isn't* a
|
||||||
// match.
|
// match.
|
||||||
let injector: LInjector|null = startInjector;
|
let injector: LInjector|null =
|
||||||
|
flags & InjectFlags.SkipSelf ? startInjector.parent ! : startInjector;
|
||||||
while (injector) {
|
while (injector) {
|
||||||
// Our bloom filter size is 256 bits, which is eight 32-bit bloom filter buckets:
|
// Our bloom filter size is 256 bits, which is eight 32-bit bloom filter buckets:
|
||||||
// bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc.
|
// bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc.
|
||||||
|
@ -472,6 +479,8 @@ export function bloomFindPossibleInjector(startInjector: LInjector, bloomBit: nu
|
||||||
// this injector is a potential match.
|
// this injector is a potential match.
|
||||||
if ((value & mask) === mask) {
|
if ((value & mask) === mask) {
|
||||||
return injector;
|
return injector;
|
||||||
|
} else if (flags & InjectFlags.Self || flags & InjectFlags.Host && !sameHostView(injector)) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the current injector does not have the directive, check the bloom filters for the ancestor
|
// If the current injector does not have the directive, check the bloom filters for the ancestor
|
||||||
|
@ -491,6 +500,16 @@ export function bloomFindPossibleInjector(startInjector: LInjector, bloomBit: nu
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the current injector and its parent are in the same host view.
|
||||||
|
*
|
||||||
|
* This is necessary to support @Host() decorators. If @Host() is set, we should stop searching once
|
||||||
|
* the injector and its parent view don't match because it means we'd cross the view boundary.
|
||||||
|
*/
|
||||||
|
function sameHostView(injector: LInjector): boolean {
|
||||||
|
return !!injector.parent && injector.parent.node.view === injector.node.view;
|
||||||
|
}
|
||||||
|
|
||||||
export class ReadFromInjectorFn<T> {
|
export class ReadFromInjectorFn<T> {
|
||||||
constructor(readonly read: (injector: LInjector, node: LNode, directiveIndex?: number) => T) {}
|
constructor(readonly read: (injector: LInjector, node: LNode, directiveIndex?: number) => T) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -584,6 +584,9 @@
|
||||||
{
|
{
|
||||||
"name": "resolveRendererType2"
|
"name": "resolveRendererType2"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "sameHostView"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "saveNameToExportMap"
|
"name": "saveNameToExportMap"
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ChangeDetectorRef, ElementRef, InjectFlags, TemplateRef, ViewContainerRef, defineInjectable} from '@angular/core';
|
import {ChangeDetectorRef, ElementRef, Host, InjectFlags, Optional, Self, SkipSelf, TemplateRef, ViewContainerRef, defineInjectable} from '@angular/core';
|
||||||
import {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
|
import {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
|
||||||
|
|
||||||
import {defineComponent} from '../../src/render3/definition';
|
import {defineComponent} from '../../src/render3/definition';
|
||||||
|
@ -129,8 +129,8 @@ describe('di', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <div dirA dirB></div> */
|
/** <div dirA dirB></div> */
|
||||||
const App = createComponent('app', function(ctx: any, cm: boolean) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (cm) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div', ['dirA', '', 'dirB', '']);
|
elementStart(0, 'div', ['dirA', '', 'dirB', '']);
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
@ -153,8 +153,8 @@ describe('di', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <comp dirB></comp> */
|
/** <comp dirB></comp> */
|
||||||
const App = createComponent('app', function(ctx: any, cm: boolean) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (cm) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'comp', ['dirB', '']);
|
elementStart(0, 'comp', ['dirB', '']);
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
@ -180,8 +180,8 @@ describe('di', () => {
|
||||||
* <div dirA dirB></div>
|
* <div dirA dirB></div>
|
||||||
* % }
|
* % }
|
||||||
*/
|
*/
|
||||||
const App = createComponent('app', function(ctx: any, cm: boolean) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (cm) {
|
if (rf & RenderFlags.Create) {
|
||||||
container(0);
|
container(0);
|
||||||
}
|
}
|
||||||
containerRefreshStart(0);
|
containerRefreshStart(0);
|
||||||
|
@ -240,8 +240,8 @@ describe('di', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <div dirA dirB dirC></div> */
|
/** <div dirA dirB dirC></div> */
|
||||||
const App = createComponent('app', function(ctx: any, cm: boolean) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (cm) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div', ['dirA', '', 'dirB', '', 'dirC', '']);
|
elementStart(0, 'div', ['dirA', '', 'dirB', '', 'dirC', '']);
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
@ -300,8 +300,8 @@ describe('di', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <comp dirA dirB dirC dirD></comp> */
|
/** <comp dirA dirB dirC dirD></comp> */
|
||||||
const App = createComponent('app', function(ctx: any, cm: boolean) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (cm) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'comp', ['dirA', '', 'dirB', '', 'dirC', '', 'dirD', '']);
|
elementStart(0, 'comp', ['dirA', '', 'dirB', '', 'dirC', '', 'dirD', '']);
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
@ -378,16 +378,16 @@ describe('di', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <div dirA dirB></div> */
|
/** <div dirA dirB></div> */
|
||||||
const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
|
const Parent = createComponent('parent', function(rf: RenderFlags, ctx: any) {
|
||||||
if (cm) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div', ['dirA', '', 'dirB', '']);
|
elementStart(0, 'div', ['dirA', '', 'dirB', '']);
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
}, [DirA, DirB]);
|
}, [DirA, DirB]);
|
||||||
|
|
||||||
/** <parent dirB></parent> */
|
/** <parent dirB></parent> */
|
||||||
const App = createComponent('app', function(ctx: any, cm: boolean) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (cm) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'parent', ['dirB', '']);
|
elementStart(0, 'parent', ['dirB', '']);
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
@ -426,7 +426,7 @@ describe('di', () => {
|
||||||
expect(fixture.html).toEqual('MyService');
|
expect(fixture.html).toEqual('MyService');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if directive is not found', () => {
|
it('should throw if directive is not found anywhere', () => {
|
||||||
class Dir {
|
class Dir {
|
||||||
constructor(siblingDir: OtherDir) {}
|
constructor(siblingDir: OtherDir) {}
|
||||||
|
|
||||||
|
@ -448,8 +448,8 @@ describe('di', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <div dir></div> */
|
/** <div dir></div> */
|
||||||
const App = createComponent('app', function(ctx: any, cm: boolean) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (cm) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div', ['dir', '']);
|
elementStart(0, 'div', ['dir', '']);
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
@ -458,6 +458,44 @@ describe('di', () => {
|
||||||
expect(() => new ComponentFixture(App)).toThrowError(/Injector: NOT_FOUND \[OtherDir\]/);
|
expect(() => new ComponentFixture(App)).toThrowError(/Injector: NOT_FOUND \[OtherDir\]/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw if directive is not found in ancestor tree', () => {
|
||||||
|
class Dir {
|
||||||
|
constructor(siblingDir: OtherDir) {}
|
||||||
|
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
selectors: [['', 'dir', '']],
|
||||||
|
type: Dir,
|
||||||
|
factory: () => new Dir(directiveInject(OtherDir)),
|
||||||
|
features: [PublicFeature]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class OtherDir {
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
selectors: [['', 'other', '']],
|
||||||
|
type: OtherDir,
|
||||||
|
factory: () => new OtherDir(),
|
||||||
|
features: [PublicFeature]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <div other></div>
|
||||||
|
* <div dir></div>
|
||||||
|
*/
|
||||||
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div', ['other', '']);
|
||||||
|
elementEnd();
|
||||||
|
elementStart(1, 'div', ['dir', '']);
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
}, [Dir, OtherDir]);
|
||||||
|
|
||||||
|
expect(() => new ComponentFixture(App)).toThrowError(/Injector: NOT_FOUND \[OtherDir\]/);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should throw if directives try to inject each other', () => {
|
it('should throw if directives try to inject each other', () => {
|
||||||
class DirA {
|
class DirA {
|
||||||
constructor(dir: DirB) {}
|
constructor(dir: DirB) {}
|
||||||
|
@ -482,8 +520,8 @@ describe('di', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <div dirA dirB></div> */
|
/** <div dirA dirB></div> */
|
||||||
const App = createComponent('app', function(ctx: any, cm: boolean) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (cm) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div', ['dirA', '', 'dirB', '']);
|
elementStart(0, 'div', ['dirA', '', 'dirB', '']);
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
@ -505,8 +543,8 @@ describe('di', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <div dir></div> */
|
/** <div dir></div> */
|
||||||
const App = createComponent('app', function(ctx: any, cm: boolean) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (cm) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div', ['dir', '']);
|
elementStart(0, 'div', ['dir', '']);
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
@ -515,6 +553,217 @@ describe('di', () => {
|
||||||
expect(() => new ComponentFixture(App)).toThrowError(/Cannot instantiate cyclic dependency!/);
|
expect(() => new ComponentFixture(App)).toThrowError(/Cannot instantiate cyclic dependency!/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('flags', () => {
|
||||||
|
|
||||||
|
class DirB {
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
type: DirB,
|
||||||
|
selectors: [['', 'dirB', '']],
|
||||||
|
factory: () => new DirB(),
|
||||||
|
inputs: {value: 'dirB'},
|
||||||
|
features: [PublicFeature]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should not throw if dependency is @Optional', () => {
|
||||||
|
let dirA: DirA;
|
||||||
|
|
||||||
|
class DirA {
|
||||||
|
constructor(@Optional() public dirB: DirB|null) {}
|
||||||
|
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
type: DirA,
|
||||||
|
selectors: [['', 'dirA', '']],
|
||||||
|
factory: () => dirA = new DirA(directiveInject(DirB, InjectFlags.Optional))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** <div dirA></div> */
|
||||||
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div', ['dirA', '']);
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
}, [DirA, DirB]);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
const fixture = new ComponentFixture(App);
|
||||||
|
expect(dirA !.dirB).toEqual(null);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not throw if dependency is @Optional but defined elsewhere', () => {
|
||||||
|
let dirA: DirA;
|
||||||
|
|
||||||
|
class DirA {
|
||||||
|
constructor(@Optional() public dirB: DirB|null) {}
|
||||||
|
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
type: DirA,
|
||||||
|
selectors: [['', 'dirA', '']],
|
||||||
|
factory: () => dirA = new DirA(directiveInject(DirB, InjectFlags.Optional))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <div dirB></div>
|
||||||
|
* <div dirA></div>
|
||||||
|
*/
|
||||||
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div', ['dirB', '']);
|
||||||
|
elementEnd();
|
||||||
|
elementStart(1, 'div', ['dirA', '']);
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
}, [DirA, DirB]);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
const fixture = new ComponentFixture(App);
|
||||||
|
expect(dirA !.dirB).toEqual(null);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip the current node with @SkipSelf', () => {
|
||||||
|
let dirA: DirA;
|
||||||
|
|
||||||
|
class DirA {
|
||||||
|
constructor(@SkipSelf() public dirB: DirB) {}
|
||||||
|
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
type: DirA,
|
||||||
|
selectors: [['', 'dirA', '']],
|
||||||
|
factory: () => dirA = new DirA(directiveInject(DirB, InjectFlags.SkipSelf))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** <div dirA dirB="self"></div> */
|
||||||
|
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div', ['dirA', '', 'dirB', 'self']);
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
}, [DirA, DirB]);
|
||||||
|
|
||||||
|
/* <comp dirB="parent"></comp> */
|
||||||
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'comp', ['dirB', 'parent']);
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
}, [Comp, DirB]);
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(App);
|
||||||
|
expect(dirA !.dirB.value).toEqual('parent');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check only the current node with @Self', () => {
|
||||||
|
let dirA: DirA;
|
||||||
|
|
||||||
|
class DirA {
|
||||||
|
constructor(@Self() public dirB: DirB) {}
|
||||||
|
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
type: DirA,
|
||||||
|
selectors: [['', 'dirA', '']],
|
||||||
|
factory: () => dirA = new DirA(directiveInject(DirB, InjectFlags.Self))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <div dirB>
|
||||||
|
* <div dirA></div>
|
||||||
|
* </div>
|
||||||
|
*/
|
||||||
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div', ['dirB', '']);
|
||||||
|
elementStart(1, 'div', ['dirA', '']);
|
||||||
|
elementEnd();
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
}, [DirA, DirB]);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
const fixture = new ComponentFixture(App);
|
||||||
|
}).toThrowError(/Injector: NOT_FOUND \[DirB\]/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check only the current node with @Self even with false positive', () => {
|
||||||
|
let dirA: DirA;
|
||||||
|
|
||||||
|
class DirA {
|
||||||
|
constructor(@Self() public dirB: DirB) {}
|
||||||
|
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
type: DirA,
|
||||||
|
selectors: [['', 'dirA', '']],
|
||||||
|
factory: () => dirA = new DirA(directiveInject(DirB, InjectFlags.Self))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const DirC = createDirective('dirC');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <div dirB>
|
||||||
|
* <div dirA dirC></div>
|
||||||
|
* </div>
|
||||||
|
*/
|
||||||
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div', ['dirB', '']);
|
||||||
|
elementStart(1, 'div', ['dirA', '', 'dirC', '']);
|
||||||
|
elementEnd();
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
}, [DirA, DirB, DirC]);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
(DirA as any)['__NG_ELEMENT_ID__'] = 1;
|
||||||
|
(DirC as any)['__NG_ELEMENT_ID__'] = 257;
|
||||||
|
const fixture = new ComponentFixture(App);
|
||||||
|
}).toThrowError(/Injector: NOT_FOUND \[DirB\]/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not pass component boundary with @Host', () => {
|
||||||
|
let dirA: DirA;
|
||||||
|
|
||||||
|
class DirA {
|
||||||
|
constructor(@Host() public dirB: DirB) {}
|
||||||
|
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
type: DirA,
|
||||||
|
selectors: [['', 'dirA', '']],
|
||||||
|
factory: () => dirA = new DirA(directiveInject(DirB, InjectFlags.Host))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** <div dirA></div> */
|
||||||
|
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div', ['dirA', '']);
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
}, [DirA, DirB]);
|
||||||
|
|
||||||
|
/* <comp dirB></comp> */
|
||||||
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'comp', ['dirB', '']);
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
}, [Comp, DirB]);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
const fixture = new ComponentFixture(App);
|
||||||
|
}).toThrowError(/Injector: NOT_FOUND \[DirB\]/);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ElementRef', () => {
|
describe('ElementRef', () => {
|
||||||
|
@ -1025,16 +1274,16 @@ describe('di', () => {
|
||||||
bloomAdd(di, { __NG_ELEMENT_ID__: 223 } as any);
|
bloomAdd(di, { __NG_ELEMENT_ID__: 223 } as any);
|
||||||
bloomAdd(di, { __NG_ELEMENT_ID__: 255 } as any);
|
bloomAdd(di, { __NG_ELEMENT_ID__: 255 } as any);
|
||||||
|
|
||||||
expect(bloomFindPossibleInjector(di, 0)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 0, InjectFlags.Default)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 1)).toEqual(null);
|
expect(bloomFindPossibleInjector(di, 1, InjectFlags.Default)).toEqual(null);
|
||||||
expect(bloomFindPossibleInjector(di, 32)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 32, InjectFlags.Default)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 64)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 64, InjectFlags.Default)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 96)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 96, InjectFlags.Default)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 127)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 127, InjectFlags.Default)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 161)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 161, InjectFlags.Default)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 188)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 188, InjectFlags.Default)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 223)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 223, InjectFlags.Default)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 255)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 255, InjectFlags.Default)).toEqual(di);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue