fix(di): instatiate services lazily
This commit is contained in:
parent
2bc1217409
commit
7531b48d02
|
@ -456,7 +456,7 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
|
|||
this._host = null;
|
||||
this._preBuiltObjects = null;
|
||||
this._strategy.callOnDestroy();
|
||||
this._injector.internalStrategy.dehydrate();
|
||||
this._strategy.dehydrate();
|
||||
}
|
||||
|
||||
onAllChangesDone(): void {
|
||||
|
@ -476,7 +476,8 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
|
|||
this._host = host;
|
||||
this._preBuiltObjects = preBuiltObjects;
|
||||
|
||||
this._hydrateInjector(imperativelyCreatedInjector, host);
|
||||
this._reattachInjectors(imperativelyCreatedInjector, host);
|
||||
this._strategy.hydrate();
|
||||
|
||||
if (isPresent(host)) {
|
||||
this._addViewQueries(host);
|
||||
|
@ -488,7 +489,7 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
|
|||
this.hydrated = true;
|
||||
}
|
||||
|
||||
private _hydrateInjector(imperativelyCreatedInjector: Injector, host: ElementInjector): void {
|
||||
private _reattachInjectors(imperativelyCreatedInjector: Injector, host: ElementInjector): void {
|
||||
if (isPresent(this._parent)) {
|
||||
this._reattachInjector(this._injector, this._parent._injector, false);
|
||||
} else {
|
||||
|
@ -539,7 +540,6 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
|
|||
|
||||
private _reattachInjector(injector: Injector, parentInjector: Injector, isBoundary: boolean) {
|
||||
injector.internalStrategy.attach(parentInjector, isBoundary);
|
||||
injector.internalStrategy.hydrate();
|
||||
}
|
||||
|
||||
getPipes(): Pipes {
|
||||
|
@ -847,6 +847,8 @@ interface _ElementInjectorStrategy {
|
|||
buildQueries(): void;
|
||||
addDirectivesMatchingQuery(q: Query, res: any[]): void;
|
||||
getComponentBinding(): DirectiveBinding;
|
||||
hydrate(): void;
|
||||
dehydrate(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -856,6 +858,48 @@ interface _ElementInjectorStrategy {
|
|||
class ElementInjectorInlineStrategy implements _ElementInjectorStrategy {
|
||||
constructor(public injectorStrategy: InjectorInlineStrategy, public _ei: ElementInjector) {}
|
||||
|
||||
hydrate(): void {
|
||||
var i = this.injectorStrategy;
|
||||
var p = i.protoStrategy;
|
||||
i.resetContructionCounter();
|
||||
|
||||
if (p.binding0 instanceof DirectiveBinding && isPresent(p.keyId0) && i.obj0 === undefinedValue)
|
||||
i.obj0 = i.instantiateBinding(p.binding0, p.visibility0);
|
||||
if (p.binding1 instanceof DirectiveBinding && isPresent(p.keyId1) && i.obj1 === undefinedValue)
|
||||
i.obj1 = i.instantiateBinding(p.binding1, p.visibility1);
|
||||
if (p.binding2 instanceof DirectiveBinding && isPresent(p.keyId2) && i.obj2 === undefinedValue)
|
||||
i.obj2 = i.instantiateBinding(p.binding2, p.visibility2);
|
||||
if (p.binding3 instanceof DirectiveBinding && isPresent(p.keyId3) && i.obj3 === undefinedValue)
|
||||
i.obj3 = i.instantiateBinding(p.binding3, p.visibility3);
|
||||
if (p.binding4 instanceof DirectiveBinding && isPresent(p.keyId4) && i.obj4 === undefinedValue)
|
||||
i.obj4 = i.instantiateBinding(p.binding4, p.visibility4);
|
||||
if (p.binding5 instanceof DirectiveBinding && isPresent(p.keyId5) && i.obj5 === undefinedValue)
|
||||
i.obj5 = i.instantiateBinding(p.binding5, p.visibility5);
|
||||
if (p.binding6 instanceof DirectiveBinding && isPresent(p.keyId6) && i.obj6 === undefinedValue)
|
||||
i.obj6 = i.instantiateBinding(p.binding6, p.visibility6);
|
||||
if (p.binding7 instanceof DirectiveBinding && isPresent(p.keyId7) && i.obj7 === undefinedValue)
|
||||
i.obj7 = i.instantiateBinding(p.binding7, p.visibility7);
|
||||
if (p.binding8 instanceof DirectiveBinding && isPresent(p.keyId8) && i.obj8 === undefinedValue)
|
||||
i.obj8 = i.instantiateBinding(p.binding8, p.visibility8);
|
||||
if (p.binding9 instanceof DirectiveBinding && isPresent(p.keyId9) && i.obj9 === undefinedValue)
|
||||
i.obj9 = i.instantiateBinding(p.binding9, p.visibility9);
|
||||
}
|
||||
|
||||
dehydrate() {
|
||||
var i = this.injectorStrategy;
|
||||
|
||||
i.obj0 = undefinedValue;
|
||||
i.obj1 = undefinedValue;
|
||||
i.obj2 = undefinedValue;
|
||||
i.obj3 = undefinedValue;
|
||||
i.obj4 = undefinedValue;
|
||||
i.obj5 = undefinedValue;
|
||||
i.obj6 = undefinedValue;
|
||||
i.obj7 = undefinedValue;
|
||||
i.obj8 = undefinedValue;
|
||||
i.obj9 = undefinedValue;
|
||||
}
|
||||
|
||||
callOnDestroy(): void {
|
||||
var i = this.injectorStrategy;
|
||||
var p = i.protoStrategy;
|
||||
|
@ -938,16 +982,46 @@ class ElementInjectorInlineStrategy implements _ElementInjectorStrategy {
|
|||
var i = this.injectorStrategy;
|
||||
var p = i.protoStrategy;
|
||||
|
||||
if (isPresent(p.binding0) && p.binding0.key.token === query.selector) list.push(i.obj0);
|
||||
if (isPresent(p.binding1) && p.binding1.key.token === query.selector) list.push(i.obj1);
|
||||
if (isPresent(p.binding2) && p.binding2.key.token === query.selector) list.push(i.obj2);
|
||||
if (isPresent(p.binding3) && p.binding3.key.token === query.selector) list.push(i.obj3);
|
||||
if (isPresent(p.binding4) && p.binding4.key.token === query.selector) list.push(i.obj4);
|
||||
if (isPresent(p.binding5) && p.binding5.key.token === query.selector) list.push(i.obj5);
|
||||
if (isPresent(p.binding6) && p.binding6.key.token === query.selector) list.push(i.obj6);
|
||||
if (isPresent(p.binding7) && p.binding7.key.token === query.selector) list.push(i.obj7);
|
||||
if (isPresent(p.binding8) && p.binding8.key.token === query.selector) list.push(i.obj8);
|
||||
if (isPresent(p.binding9) && p.binding9.key.token === query.selector) list.push(i.obj9);
|
||||
if (isPresent(p.binding0) && p.binding0.key.token === query.selector) {
|
||||
if (i.obj0 === undefinedValue) i.obj0 = i.instantiateBinding(p.binding0, p.visibility0);
|
||||
list.push(i.obj0);
|
||||
}
|
||||
if (isPresent(p.binding1) && p.binding1.key.token === query.selector) {
|
||||
if (i.obj1 === undefinedValue) i.obj1 = i.instantiateBinding(p.binding1, p.visibility1);
|
||||
list.push(i.obj1);
|
||||
}
|
||||
if (isPresent(p.binding2) && p.binding2.key.token === query.selector) {
|
||||
if (i.obj2 === undefinedValue) i.obj2 = i.instantiateBinding(p.binding2, p.visibility2);
|
||||
list.push(i.obj2);
|
||||
}
|
||||
if (isPresent(p.binding3) && p.binding3.key.token === query.selector) {
|
||||
if (i.obj3 === undefinedValue) i.obj3 = i.instantiateBinding(p.binding3, p.visibility3);
|
||||
list.push(i.obj3);
|
||||
}
|
||||
if (isPresent(p.binding4) && p.binding4.key.token === query.selector) {
|
||||
if (i.obj4 === undefinedValue) i.obj4 = i.instantiateBinding(p.binding4, p.visibility4);
|
||||
list.push(i.obj4);
|
||||
}
|
||||
if (isPresent(p.binding5) && p.binding5.key.token === query.selector) {
|
||||
if (i.obj5 === undefinedValue) i.obj5 = i.instantiateBinding(p.binding5, p.visibility5);
|
||||
list.push(i.obj5);
|
||||
}
|
||||
if (isPresent(p.binding6) && p.binding6.key.token === query.selector) {
|
||||
if (i.obj6 === undefinedValue) i.obj6 = i.instantiateBinding(p.binding6, p.visibility6);
|
||||
list.push(i.obj6);
|
||||
}
|
||||
if (isPresent(p.binding7) && p.binding7.key.token === query.selector) {
|
||||
if (i.obj7 === undefinedValue) i.obj7 = i.instantiateBinding(p.binding7, p.visibility7);
|
||||
list.push(i.obj7);
|
||||
}
|
||||
if (isPresent(p.binding8) && p.binding8.key.token === query.selector) {
|
||||
if (i.obj8 === undefinedValue) i.obj8 = i.instantiateBinding(p.binding8, p.visibility8);
|
||||
list.push(i.obj8);
|
||||
}
|
||||
if (isPresent(p.binding9) && p.binding9.key.token === query.selector) {
|
||||
if (i.obj9 === undefinedValue) i.obj9 = i.instantiateBinding(p.binding9, p.visibility9);
|
||||
list.push(i.obj9);
|
||||
}
|
||||
}
|
||||
|
||||
getComponentBinding(): DirectiveBinding {
|
||||
|
@ -963,6 +1037,23 @@ class ElementInjectorInlineStrategy implements _ElementInjectorStrategy {
|
|||
class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy {
|
||||
constructor(public injectorStrategy: InjectorDynamicStrategy, public _ei: ElementInjector) {}
|
||||
|
||||
hydrate(): void {
|
||||
var inj = this.injectorStrategy;
|
||||
var p = inj.protoStrategy;
|
||||
|
||||
for (var i = 0; i < p.keyIds.length; i++) {
|
||||
if (p.bindings[i] instanceof DirectiveBinding && isPresent(p.keyIds[i]) &&
|
||||
inj.objs[i] === undefinedValue) {
|
||||
inj.objs[i] = inj.instantiateBinding(p.bindings[i], p.visibilities[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dehydrate(): void {
|
||||
var inj = this.injectorStrategy;
|
||||
ListWrapper.fill(inj.objs, undefinedValue);
|
||||
}
|
||||
|
||||
callOnDestroy(): void {
|
||||
var ist = this.injectorStrategy;
|
||||
var p = ist.protoStrategy;
|
||||
|
@ -983,7 +1074,8 @@ class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy {
|
|||
}
|
||||
|
||||
buildQueries(): void {
|
||||
var p = this.injectorStrategy.protoStrategy;
|
||||
var inj = this.injectorStrategy;
|
||||
var p = inj.protoStrategy;
|
||||
|
||||
for (var i = 0; i < p.bindings.length; i++) {
|
||||
if (p.bindings[i] instanceof DirectiveBinding) {
|
||||
|
@ -997,7 +1089,12 @@ class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy {
|
|||
var p = ist.protoStrategy;
|
||||
|
||||
for (var i = 0; i < p.bindings.length; i++) {
|
||||
if (p.bindings[i].key.token === query.selector) list.push(ist.objs[i]);
|
||||
if (p.bindings[i].key.token === query.selector) {
|
||||
if (ist.objs[i] === undefinedValue) {
|
||||
ist.objs[i] = ist.instantiateBinding(p.bindings[i], p.visibilities[i]);
|
||||
}
|
||||
list.push(ist.objs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -192,8 +192,8 @@ export interface InjectorStrategy {
|
|||
getMaxNumberOfObjects(): number;
|
||||
|
||||
attach(parent: Injector, isBoundary: boolean): void;
|
||||
hydrate(): void;
|
||||
dehydrate(): void;
|
||||
resetContructionCounter(): void;
|
||||
instantiateBinding(binding: ResolvedBinding, visibility: number): any;
|
||||
}
|
||||
|
||||
export class InjectorInlineStrategy implements InjectorStrategy {
|
||||
|
@ -210,33 +210,10 @@ export class InjectorInlineStrategy implements InjectorStrategy {
|
|||
|
||||
constructor(public injector: Injector, public protoStrategy: ProtoInjectorInlineStrategy) {}
|
||||
|
||||
hydrate(): void {
|
||||
var p = this.protoStrategy;
|
||||
var inj = this.injector;
|
||||
resetContructionCounter(): void { this.injector._constructionCounter = 0; }
|
||||
|
||||
inj._constructionCounter = 0;
|
||||
|
||||
|
||||
if (isPresent(p.keyId0) && this.obj0 === undefinedValue)
|
||||
this.obj0 = inj._new(p.binding0, p.visibility0);
|
||||
if (isPresent(p.keyId1) && this.obj1 === undefinedValue)
|
||||
this.obj1 = inj._new(p.binding1, p.visibility1);
|
||||
if (isPresent(p.keyId2) && this.obj2 === undefinedValue)
|
||||
this.obj2 = inj._new(p.binding2, p.visibility2);
|
||||
if (isPresent(p.keyId3) && this.obj3 === undefinedValue)
|
||||
this.obj3 = inj._new(p.binding3, p.visibility3);
|
||||
if (isPresent(p.keyId4) && this.obj4 === undefinedValue)
|
||||
this.obj4 = inj._new(p.binding4, p.visibility4);
|
||||
if (isPresent(p.keyId5) && this.obj5 === undefinedValue)
|
||||
this.obj5 = inj._new(p.binding5, p.visibility5);
|
||||
if (isPresent(p.keyId6) && this.obj6 === undefinedValue)
|
||||
this.obj6 = inj._new(p.binding6, p.visibility6);
|
||||
if (isPresent(p.keyId7) && this.obj7 === undefinedValue)
|
||||
this.obj7 = inj._new(p.binding7, p.visibility7);
|
||||
if (isPresent(p.keyId8) && this.obj8 === undefinedValue)
|
||||
this.obj8 = inj._new(p.binding8, p.visibility8);
|
||||
if (isPresent(p.keyId9) && this.obj9 === undefinedValue)
|
||||
this.obj9 = inj._new(p.binding9, p.visibility9);
|
||||
instantiateBinding(binding: ResolvedBinding, visibility: number): any {
|
||||
return this.injector._new(binding, visibility);
|
||||
}
|
||||
|
||||
attach(parent: Injector, isBoundary: boolean): void {
|
||||
|
@ -245,19 +222,6 @@ export class InjectorInlineStrategy implements InjectorStrategy {
|
|||
inj._isBoundary = isBoundary;
|
||||
}
|
||||
|
||||
dehydrate() {
|
||||
this.obj0 = undefinedValue;
|
||||
this.obj1 = undefinedValue;
|
||||
this.obj2 = undefinedValue;
|
||||
this.obj3 = undefinedValue;
|
||||
this.obj4 = undefinedValue;
|
||||
this.obj5 = undefinedValue;
|
||||
this.obj6 = undefinedValue;
|
||||
this.obj7 = undefinedValue;
|
||||
this.obj8 = undefinedValue;
|
||||
this.obj9 = undefinedValue;
|
||||
}
|
||||
|
||||
getObjByKeyId(keyId: number, visibility: number): any {
|
||||
var p = this.protoStrategy;
|
||||
var inj = this.injector;
|
||||
|
@ -352,13 +316,10 @@ export class InjectorDynamicStrategy implements InjectorStrategy {
|
|||
ListWrapper.fill(this.objs, undefinedValue);
|
||||
}
|
||||
|
||||
hydrate(): void {
|
||||
var p = this.protoStrategy;
|
||||
for (var i = 0; i < p.keyIds.length; i++) {
|
||||
if (isPresent(p.keyIds[i]) && this.objs[i] === undefinedValue) {
|
||||
this.objs[i] = this.injector._new(p.bindings[i], p.visibilities[i]);
|
||||
}
|
||||
}
|
||||
resetContructionCounter(): void { this.injector._constructionCounter = 0; }
|
||||
|
||||
instantiateBinding(binding: ResolvedBinding, visibility: number): any {
|
||||
return this.injector._new(binding, visibility);
|
||||
}
|
||||
|
||||
attach(parent: Injector, isBoundary: boolean): void {
|
||||
|
@ -367,8 +328,6 @@ export class InjectorDynamicStrategy implements InjectorStrategy {
|
|||
inj._isBoundary = isBoundary;
|
||||
}
|
||||
|
||||
dehydrate(): void { ListWrapper.fill(this.objs, undefinedValue); }
|
||||
|
||||
getObjByKeyId(keyId: number, visibility: number): any {
|
||||
var p = this.protoStrategy;
|
||||
|
||||
|
|
|
@ -661,17 +661,49 @@ export function main() {
|
|||
expect(inj.get(NeedsService).service).toEqual('service');
|
||||
});
|
||||
|
||||
it("should not instantiate other directives that depend on viewInjector bindings",
|
||||
() => {
|
||||
var directiveAnnotation = new dirAnn.Component({
|
||||
viewInjector: ListWrapper.concat([bind("service").toValue("service")], extraBindings)
|
||||
});
|
||||
var componentDirective =
|
||||
DirectiveBinding.createFromType(SimpleDirective, directiveAnnotation);
|
||||
expect(() => { injector([componentDirective, NeedsService], null); })
|
||||
.toThrowError(containsRegexp(
|
||||
`No provider for service! (${stringify(NeedsService) } -> service)`));
|
||||
it("should instantiate hostInjector injectables lazily", () => {
|
||||
var created = false;
|
||||
var inj = injector(
|
||||
ListWrapper.concat([DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component({
|
||||
hostInjector: [bind('service').toFactory(() => created = true)]
|
||||
}))],
|
||||
extraBindings),
|
||||
null, true);
|
||||
|
||||
expect(created).toBe(false);
|
||||
|
||||
inj.get('service');
|
||||
|
||||
expect(created).toBe(true);
|
||||
});
|
||||
|
||||
it("should instantiate viewInjector injectables lazily", () => {
|
||||
var created = false;
|
||||
var inj = injector(
|
||||
ListWrapper.concat([DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component({
|
||||
viewInjector: [bind('service').toFactory(() => created = true)]
|
||||
}))],
|
||||
extraBindings),
|
||||
null, true);
|
||||
|
||||
expect(created).toBe(false);
|
||||
|
||||
inj.get('service');
|
||||
|
||||
expect(created).toBe(true);
|
||||
});
|
||||
|
||||
it("should not instantiate other directives that depend on viewInjector bindings",
|
||||
() => {
|
||||
var directiveAnnotation = new dirAnn.Component({
|
||||
viewInjector: ListWrapper.concat([bind("service").toValue("service")], extraBindings)
|
||||
});
|
||||
var componentDirective =
|
||||
DirectiveBinding.createFromType(SimpleDirective, directiveAnnotation);
|
||||
expect(() => { injector([componentDirective, NeedsService], null); })
|
||||
.toThrowError(containsRegexp(
|
||||
`No provider for service! (${stringify(NeedsService) } -> service)`));
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on hostInjector bindings of other directives", () => {
|
||||
var shadowInj = hostShadowInjectors(
|
||||
|
|
|
@ -1103,6 +1103,32 @@ export function main() {
|
|||
expect(parent.grandParentBus).toBe(grandParent.bus);
|
||||
expect(child.bus).toBe(parent.bus);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should create viewInjector injectables lazily",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp, new viewAnn.View({
|
||||
template: `
|
||||
<component-providing-logging-injectable #providing>
|
||||
<directive-consuming-injectable *ng-if="ctxBoolProp">
|
||||
</directive-consuming-injectable>
|
||||
</component-providing-logging-injectable>
|
||||
`,
|
||||
directives:
|
||||
[DirectiveConsumingInjectable, ComponentProvidingLoggingInjectable, NgIf]
|
||||
}))
|
||||
.createAsync(MyComp)
|
||||
.then((rootTC) => {
|
||||
var providing = rootTC.componentViewChildren[0].getLocal("providing");
|
||||
expect(providing.created).toBe(false);
|
||||
|
||||
rootTC.componentInstance.ctxBoolProp = true;
|
||||
rootTC.detectChanges();
|
||||
|
||||
expect(providing.created).toBe(true);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
@ -1701,6 +1727,23 @@ class DirectiveWithTwoWayBinding {
|
|||
class InjectableService {
|
||||
}
|
||||
|
||||
function createInjectableWithLogging(inj: Injector) {
|
||||
inj.get(ComponentProvidingLoggingInjectable).created = true;
|
||||
return new InjectableService();
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'component-providing-logging-injectable',
|
||||
hostInjector:
|
||||
[new Binding(InjectableService, {toFactory: createInjectableWithLogging, deps: [Injector]})]
|
||||
})
|
||||
@View({template: ''})
|
||||
@Injectable()
|
||||
class ComponentProvidingLoggingInjectable {
|
||||
created: boolean = false;
|
||||
}
|
||||
|
||||
|
||||
@Directive({selector: 'directive-providing-injectable', hostInjector: [[InjectableService]]})
|
||||
@Injectable()
|
||||
class DirectiveProvidingInjectable {
|
||||
|
|
Loading…
Reference in New Issue