feat(ElementInjector): add support for "special" objects
This commit is contained in:
parent
e3548b497f
commit
79d270c3dd
|
@ -9,7 +9,7 @@ export function run () {
|
|||
var bindings = [A, B, C];
|
||||
var proto = new ProtoElementInjector(null, bindings, []);
|
||||
for (var i = 0; i < 20000; ++i) {
|
||||
var ei = proto.instantiate();
|
||||
var ei = proto.instantiate({view:null});
|
||||
ei.instantiateDirectives(appInjector);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ export function run () {
|
|||
|
||||
var bindings = [A, B, C];
|
||||
var proto = new ProtoElementInjector(null, bindings, []);
|
||||
var ei = proto.instantiate();
|
||||
var ei = proto.instantiate({view:null});
|
||||
|
||||
for (var i = 0; i < 20000; ++i) {
|
||||
ei.clearDirectives();
|
||||
|
|
|
@ -3,8 +3,10 @@ import {Math} from 'facade/math';
|
|||
import {List, ListWrapper} from 'facade/collection';
|
||||
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError} from 'di/di';
|
||||
import {Parent, Ancestor} from 'core/annotations/visibility';
|
||||
import {StaticKeys} from './static_keys';
|
||||
|
||||
var MAX_DEPTH = Math.pow(2, 30) - 1;
|
||||
var _undefined = new Object();
|
||||
|
||||
class TreeNode {
|
||||
@FIELD('_parent:TreeNode')
|
||||
|
@ -180,12 +182,13 @@ export class ProtoElementInjector extends TreeNode {
|
|||
this.hasProperties = false;
|
||||
}
|
||||
|
||||
instantiate():ElementInjector {
|
||||
instantiate({view}):ElementInjector {
|
||||
var p = this._parent;
|
||||
var parentElementInjector = p == null ? null : p._elementInjector;
|
||||
var parentElementInjector = p === null ? null : p._elementInjector;
|
||||
this._elementInjector = new ElementInjector({
|
||||
proto: this,
|
||||
parent: parentElementInjector
|
||||
parent: parentElementInjector,
|
||||
view: view
|
||||
});
|
||||
return this._elementInjector;
|
||||
}
|
||||
|
@ -261,9 +264,11 @@ export class ElementInjector extends TreeNode {
|
|||
@FIELD('_obj7:Object')
|
||||
@FIELD('_obj8:Object')
|
||||
@FIELD('_obj9:Object')
|
||||
constructor({proto, parent}) {
|
||||
@FIELD('_view:View')
|
||||
constructor({proto, parent, view}) {
|
||||
super(parent);
|
||||
this._proto = proto;
|
||||
this._view = view;
|
||||
|
||||
//we cannot call clearDirectives because fields won't be detected
|
||||
this._appInjector = null;
|
||||
|
@ -358,20 +363,46 @@ export class ElementInjector extends TreeNode {
|
|||
return this._getByKey(dep.key, dep.depth);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* It is fairly easy to annotate keys with metadata.
|
||||
* For example, key.metadata = 'directive'.
|
||||
*
|
||||
* This would allows to do the lookup more efficiently.
|
||||
*
|
||||
* for example
|
||||
* we would lookup special objects only when metadata = 'special'
|
||||
* we would lookup directives only when metadata = 'directive'
|
||||
*
|
||||
* Write benchmarks before doing this optimization.
|
||||
*/
|
||||
_getByKey(key:Key, depth:int) {
|
||||
var ei = this;
|
||||
while (ei != null && depth >= 0) {
|
||||
var obj = ei._getDirectiveByKey(key);
|
||||
if (isPresent(obj)) return obj;
|
||||
var specObj = ei._getSpecialObjectByKey(key);
|
||||
if (specObj !== _undefined) return specObj;
|
||||
|
||||
var dir = ei._getDirectiveByKey(key);
|
||||
if (dir !== _undefined) return dir;
|
||||
|
||||
ei = ei._parent;
|
||||
depth -= 1;
|
||||
}
|
||||
return this._appInjector.get(key);
|
||||
}
|
||||
|
||||
_getSpecialObjectByKey(key:Key) {
|
||||
var staticKeys = StaticKeys.instance();
|
||||
var keyId = key.id;
|
||||
|
||||
if (keyId === staticKeys.viewId) return this._view;
|
||||
//TODO add other objects as needed
|
||||
return _undefined;
|
||||
}
|
||||
|
||||
_getDirectiveByKey(key:Key) {
|
||||
var p = this._proto;
|
||||
var keyId= key.id;
|
||||
var keyId = key.id;
|
||||
if (p._keyId0 === keyId) return this._obj0;
|
||||
if (p._keyId1 === keyId) return this._obj1;
|
||||
if (p._keyId2 === keyId) return this._obj2;
|
||||
|
@ -382,7 +413,7 @@ export class ElementInjector extends TreeNode {
|
|||
if (p._keyId7 === keyId) return this._obj7;
|
||||
if (p._keyId8 === keyId) return this._obj8;
|
||||
if (p._keyId9 === keyId) return this._obj9;
|
||||
return null;
|
||||
return _undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import {View} from 'core/compiler/view';
|
||||
import {Key} from 'di/di';
|
||||
import {isBlank} from 'facade/lang';
|
||||
|
||||
var _staticKeys;
|
||||
|
||||
export class StaticKeys {
|
||||
constructor() {
|
||||
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
|
||||
this.viewId = Key.get(View).id;
|
||||
}
|
||||
|
||||
static instance() {
|
||||
if (isBlank(_staticKeys)) _staticKeys = new StaticKeys();
|
||||
return _staticKeys;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach} from 'test_lib/test_lib';
|
||||
import {isBlank, FIELD} from 'facade/lang';
|
||||
import {isBlank, FIELD, IMPLEMENTS} from 'facade/lang';
|
||||
import {ListWrapper, MapWrapper, List} from 'facade/collection';
|
||||
import {ProtoElementInjector} from 'core/compiler/element_injector';
|
||||
import {ProtoElementInjector, VIEW_KEY} from 'core/compiler/element_injector';
|
||||
import {Parent, Ancestor} from 'core/annotations/visibility';
|
||||
import {Injector, Inject, bind} from 'di/di';
|
||||
import {View} from 'core/compiler/view';
|
||||
|
||||
@IMPLEMENTS(View)
|
||||
class DummyView {}
|
||||
|
||||
class Directive {
|
||||
}
|
||||
|
@ -36,6 +40,13 @@ class NeedsService {
|
|||
}
|
||||
}
|
||||
|
||||
class NeedsView {
|
||||
@FIELD("view:Object")
|
||||
constructor(@Inject(View) view) {
|
||||
this.view = view;
|
||||
}
|
||||
}
|
||||
|
||||
export function main() {
|
||||
function humanize(tree, names:List) {
|
||||
var lookupName = (item) =>
|
||||
|
@ -47,6 +58,30 @@ export function main() {
|
|||
return [lookupName(tree), children];
|
||||
}
|
||||
|
||||
function injector(bindings, appInjector = null, props = null) {
|
||||
if (isBlank(appInjector)) appInjector = new Injector([]);
|
||||
if (isBlank(props)) props = {};
|
||||
|
||||
var proto = new ProtoElementInjector(null, bindings, []);
|
||||
var inj = proto.instantiate({view: props["view"]});
|
||||
inj.instantiateDirectives(appInjector);
|
||||
return inj;
|
||||
}
|
||||
|
||||
function parentChildInjectors(parentBindings, childBindings) {
|
||||
var inj = new Injector([]);
|
||||
|
||||
var protoParent = new ProtoElementInjector(null, parentBindings, []);
|
||||
var parent = protoParent.instantiate({view: null});
|
||||
parent.instantiateDirectives(inj);
|
||||
|
||||
var protoChild = new ProtoElementInjector(protoParent, childBindings, []);
|
||||
var child = protoChild.instantiate({view: null});
|
||||
child.instantiateDirectives(inj);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
describe("ElementInjector", function () {
|
||||
describe("proto injectors", function () {
|
||||
it("should construct a proto tree", function () {
|
||||
|
@ -68,9 +103,9 @@ export function main() {
|
|||
var protoChild1 = new ProtoElementInjector(protoParent, [], []);
|
||||
var protoChild2 = new ProtoElementInjector(protoParent, [], []);
|
||||
|
||||
var p = protoParent.instantiate();
|
||||
var c1 = protoChild1.instantiate();
|
||||
var c2 = protoChild2.instantiate();
|
||||
var p = protoParent.instantiate({view: null});
|
||||
var c1 = protoChild1.instantiate({view: null});
|
||||
var c2 = protoChild2.instantiate({view: null});
|
||||
|
||||
expect(humanize(p, [
|
||||
[p, 'parent'],
|
||||
|
@ -93,29 +128,6 @@ export function main() {
|
|||
});
|
||||
|
||||
describe("instantiateDirectives", function () {
|
||||
function injector(bindings, appInjector = null) {
|
||||
var proto = new ProtoElementInjector(null, bindings, []);
|
||||
var inj = proto.instantiate();
|
||||
|
||||
if (isBlank(appInjector)) appInjector = new Injector([]);
|
||||
inj.instantiateDirectives(appInjector);
|
||||
return inj;
|
||||
}
|
||||
|
||||
function parentChildInjectors(parentBindings, childBindings) {
|
||||
var inj = new Injector([]);
|
||||
|
||||
var protoParent = new ProtoElementInjector(null, parentBindings, []);
|
||||
var parent = protoParent.instantiate();
|
||||
parent.instantiateDirectives(inj);
|
||||
|
||||
var protoChild = new ProtoElementInjector(protoParent, childBindings, []);
|
||||
var child = protoChild.instantiate();
|
||||
child.instantiateDirectives(inj);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
it("should instantiate directives that have no dependencies", function () {
|
||||
var inj = injector([Directive]);
|
||||
expect(inj.get(Directive)).toBeAnInstanceOf(Directive);
|
||||
|
@ -141,6 +153,13 @@ export function main() {
|
|||
expect(d.service).toEqual("service");
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on speical objects", function () {
|
||||
var view = new DummyView();
|
||||
var inj = injector([NeedsView], null, {"view" : view});
|
||||
|
||||
expect(inj.get(NeedsView).view).toBe(view);
|
||||
});
|
||||
|
||||
it("should return app services", function () {
|
||||
var appInjector = new Injector([
|
||||
bind("service").toValue("service")
|
||||
|
@ -173,5 +192,14 @@ export function main() {
|
|||
toThrowError('No provider for Directive! (NeedDirectiveFromParent -> Directive)');
|
||||
});
|
||||
});
|
||||
|
||||
describe("special objects", function () {
|
||||
it("should return view", function () {
|
||||
var view = new DummyView();
|
||||
var inj = injector([], null, {"view" : view});
|
||||
|
||||
expect(inj.get(View)).toEqual(view);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue