feat(element_injector): add distance to propertly implement @parent
This commit is contained in:
parent
bed4b52a63
commit
3c692a1b85
|
@ -165,9 +165,11 @@ export class ProtoElementInjector {
|
||||||
parent:ProtoElementInjector;
|
parent:ProtoElementInjector;
|
||||||
index:int;
|
index:int;
|
||||||
view:View;
|
view:View;
|
||||||
constructor(parent:ProtoElementInjector, index:int, bindings:List, firstBindingIsComponent:boolean = false) {
|
distanceToParent:number;
|
||||||
|
constructor(parent:ProtoElementInjector, index:int, bindings:List, firstBindingIsComponent:boolean = false, distanceToParent:number = 0) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
this.distanceToParent = distanceToParent;
|
||||||
|
|
||||||
this._binding0IsComponent = firstBindingIsComponent;
|
this._binding0IsComponent = firstBindingIsComponent;
|
||||||
this._binding0 = null; this._keyId0 = null;
|
this._binding0 = null; this._keyId0 = null;
|
||||||
|
@ -331,6 +333,10 @@ export class ElementInjector extends TreeNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
directParent(): ElementInjector {
|
||||||
|
return this._proto.distanceToParent < 2 ? this.parent : null;
|
||||||
|
}
|
||||||
|
|
||||||
_isComponentKey(key:Key) {
|
_isComponentKey(key:Key) {
|
||||||
return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
|
return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
|
||||||
}
|
}
|
||||||
|
@ -396,11 +402,11 @@ export class ElementInjector extends TreeNode {
|
||||||
*
|
*
|
||||||
* Write benchmarks before doing this optimization.
|
* Write benchmarks before doing this optimization.
|
||||||
*/
|
*/
|
||||||
_getByKey(key:Key, depth:int, requestor:Key) {
|
_getByKey(key:Key, depth:number, requestor:Key) {
|
||||||
var ei = this;
|
var ei = this;
|
||||||
|
|
||||||
if (! this._shouldIncludeSelf(depth)) {
|
if (! this._shouldIncludeSelf(depth)) {
|
||||||
depth -= 1;
|
depth -= ei._proto.distanceToParent;
|
||||||
ei = ei._parent;
|
ei = ei._parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,8 +417,8 @@ export class ElementInjector extends TreeNode {
|
||||||
var dir = ei._getDirectiveByKeyId(key.id);
|
var dir = ei._getDirectiveByKeyId(key.id);
|
||||||
if (dir !== _undefined) return dir;
|
if (dir !== _undefined) return dir;
|
||||||
|
|
||||||
|
depth -= ei._proto.distanceToParent;
|
||||||
ei = ei._parent;
|
ei = ei._parent;
|
||||||
depth -= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPresent(this._host) && this._host._isComponentKey(key)) {
|
if (isPresent(this._host) && this._host._isComponentKey(key)) {
|
||||||
|
@ -440,7 +446,7 @@ export class ElementInjector extends TreeNode {
|
||||||
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
|
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
|
||||||
if (keyId === staticKeys.viewPortId) return this._preBuiltObjects.viewPort;
|
if (keyId === staticKeys.viewPortId) return this._preBuiltObjects.viewPort;
|
||||||
if (keyId === staticKeys.destinationLightDomId) {
|
if (keyId === staticKeys.destinationLightDomId) {
|
||||||
var p:ElementInjector = this._parent;
|
var p:ElementInjector = this.directParent();
|
||||||
return isPresent(p) ? p._preBuiltObjects.lightDom : null;
|
return isPresent(p) ? p._preBuiltObjects.lightDom : null;
|
||||||
}
|
}
|
||||||
if (keyId === staticKeys.sourceLightDomId) {
|
if (keyId === staticKeys.sourceLightDomId) {
|
||||||
|
|
|
@ -32,6 +32,7 @@ export class CompileElement {
|
||||||
inheritedProtoView:ProtoView;
|
inheritedProtoView:ProtoView;
|
||||||
inheritedProtoElementInjector:ProtoElementInjector;
|
inheritedProtoElementInjector:ProtoElementInjector;
|
||||||
inheritedElementBinder:ElementBinder;
|
inheritedElementBinder:ElementBinder;
|
||||||
|
distanceToParentInjector:number;
|
||||||
compileChildren: boolean;
|
compileChildren: boolean;
|
||||||
constructor(element:Element) {
|
constructor(element:Element) {
|
||||||
this.element = element;
|
this.element = element;
|
||||||
|
@ -55,6 +56,7 @@ export class CompileElement {
|
||||||
// inherited down to children if they don't have
|
// inherited down to children if they don't have
|
||||||
// an own elementBinder
|
// an own elementBinder
|
||||||
this.inheritedElementBinder = null;
|
this.inheritedElementBinder = null;
|
||||||
|
this.distanceToParentInjector = 0;
|
||||||
this.compileChildren = true;
|
this.compileChildren = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,15 @@ import {CompileControl} from './compile_control';
|
||||||
*/
|
*/
|
||||||
export class ProtoElementInjectorBuilder extends CompileStep {
|
export class ProtoElementInjectorBuilder extends CompileStep {
|
||||||
// public so that we can overwrite it in tests
|
// public so that we can overwrite it in tests
|
||||||
internalCreateProtoElementInjector(parent, index, directives, firstBindingIsComponent) {
|
internalCreateProtoElementInjector(parent, index, directives, firstBindingIsComponent, distance) {
|
||||||
return new ProtoElementInjector(parent, index, directives, firstBindingIsComponent);
|
return new ProtoElementInjector(parent, index, directives, firstBindingIsComponent, distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||||
var inheritedProtoElementInjector = null;
|
var distanceToParentInjector = this._getDistanceToParentInjector(parent, current);
|
||||||
var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current);
|
var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current);
|
||||||
var injectorBindings = this._collectDirectiveBindings(current);
|
var injectorBindings = this._collectDirectiveBindings(current);
|
||||||
|
|
||||||
// TODO: add lightDomServices as well,
|
// TODO: add lightDomServices as well,
|
||||||
// but after the directives as we rely on that order
|
// but after the directives as we rely on that order
|
||||||
// in the element_binder_builder.
|
// in the element_binder_builder.
|
||||||
|
@ -39,13 +40,21 @@ export class ProtoElementInjectorBuilder extends CompileStep {
|
||||||
if (injectorBindings.length > 0) {
|
if (injectorBindings.length > 0) {
|
||||||
var protoView = current.inheritedProtoView;
|
var protoView = current.inheritedProtoView;
|
||||||
var hasComponent = isPresent(current.componentDirective);
|
var hasComponent = isPresent(current.componentDirective);
|
||||||
inheritedProtoElementInjector = this.internalCreateProtoElementInjector(
|
|
||||||
parentProtoElementInjector, protoView.elementBinders.length, injectorBindings, hasComponent
|
current.inheritedProtoElementInjector = this.internalCreateProtoElementInjector(
|
||||||
|
parentProtoElementInjector, protoView.elementBinders.length, injectorBindings,
|
||||||
|
hasComponent, distanceToParentInjector
|
||||||
);
|
);
|
||||||
|
current.distanceToParentInjector = 0;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
inheritedProtoElementInjector = parentProtoElementInjector;
|
current.inheritedProtoElementInjector = parentProtoElementInjector;
|
||||||
|
current.distanceToParentInjector = distanceToParentInjector;
|
||||||
}
|
}
|
||||||
current.inheritedProtoElementInjector = inheritedProtoElementInjector;
|
}
|
||||||
|
|
||||||
|
_getDistanceToParentInjector(parent, current) {
|
||||||
|
return isPresent(parent) ? parent.distanceToParentInjector + 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getParentProtoElementInjector(parent, current) {
|
_getParentProtoElementInjector(parent, current) {
|
||||||
|
|
|
@ -103,7 +103,7 @@ export function main() {
|
||||||
|
|
||||||
parent.instantiateDirectives(inj, null, parentPreBuildObjects);
|
parent.instantiateDirectives(inj, null, parentPreBuildObjects);
|
||||||
|
|
||||||
var protoChild = new ProtoElementInjector(protoParent, 1, childBindings);
|
var protoChild = new ProtoElementInjector(protoParent, 1, childBindings, false, 1);
|
||||||
var child = protoChild.instantiate(parent, null);
|
var child = protoChild.instantiate(parent, null);
|
||||||
child.instantiateDirectives(inj, null, defaultPreBuiltObjects);
|
child.instantiateDirectives(inj, null, defaultPreBuiltObjects);
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ export function main() {
|
||||||
var host = protoParent.instantiate(null, null);
|
var host = protoParent.instantiate(null, null);
|
||||||
host.instantiateDirectives(inj, shadowInj, hostPreBuildObjects);
|
host.instantiateDirectives(inj, shadowInj, hostPreBuildObjects);
|
||||||
|
|
||||||
var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false);
|
var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false, 1);
|
||||||
var shadow = protoChild.instantiate(null, host);
|
var shadow = protoChild.instantiate(null, host);
|
||||||
shadow.instantiateDirectives(shadowInj, null, null);
|
shadow.instantiateDirectives(shadowInj, null, null);
|
||||||
|
|
||||||
|
@ -144,6 +144,30 @@ export function main() {
|
||||||
[c2, 'child2']
|
[c2, 'child2']
|
||||||
])).toEqual(["parent", ["child1", "child2"]]);
|
])).toEqual(["parent", ["child1", "child2"]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("direct parent", () => {
|
||||||
|
it("should return parent injector when distance is 1", () => {
|
||||||
|
var distance = 1;
|
||||||
|
var protoParent = new ProtoElementInjector(null, 0, []);
|
||||||
|
var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
|
||||||
|
|
||||||
|
var p = protoParent.instantiate(null, null);
|
||||||
|
var c = protoChild.instantiate(p, null);
|
||||||
|
|
||||||
|
expect(c.directParent()).toEqual(p);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return null otherwise", () => {
|
||||||
|
var distance = 2;
|
||||||
|
var protoParent = new ProtoElementInjector(null, 0, []);
|
||||||
|
var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
|
||||||
|
|
||||||
|
var p = protoParent.instantiate(null, null);
|
||||||
|
var c = protoChild.instantiate(p, null);
|
||||||
|
|
||||||
|
expect(c.directParent()).toEqual(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("hasBindings", function () {
|
describe("hasBindings", function () {
|
||||||
|
|
|
@ -128,7 +128,6 @@ export function main() {
|
||||||
expect(DOM.getText(view.nodes[0])).toEqual('Before LightDOM After');
|
expect(DOM.getText(view.nodes[0])).toEqual('Before LightDOM After');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,32 +73,57 @@ export function main() {
|
||||||
expect(creationArgs['index']).toBe(protoView.elementBinders.length);
|
expect(creationArgs['index']).toBe(protoView.elementBinders.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should inherit the ProtoElementInjector down to children without directives', () => {
|
describe("inheritedProtoElementInjector", () => {
|
||||||
var directives = [SomeDecoratorDirective];
|
it('should inherit the ProtoElementInjector down to children without directives', () => {
|
||||||
var results = createPipeline(directives).process(createElement('<div directives><span></span></div>'));
|
var directives = [SomeDecoratorDirective];
|
||||||
expect(results[1].inheritedProtoElementInjector).toBe(results[0].inheritedProtoElementInjector);
|
var results = createPipeline(directives).process(createElement('<div directives><span></span></div>'));
|
||||||
|
expect(results[1].inheritedProtoElementInjector).toBe(results[0].inheritedProtoElementInjector);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the ProtoElementInjector of the parent element as parent', () => {
|
||||||
|
var el = createElement('<div directives><span><a directives></a></span></div>');
|
||||||
|
var directives = [SomeDecoratorDirective];
|
||||||
|
var results = createPipeline(directives).process(el);
|
||||||
|
expect(results[2].inheritedProtoElementInjector.parent).toBe(
|
||||||
|
results[0].inheritedProtoElementInjector);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use a null parent for viewRoots', () => {
|
||||||
|
var el = createElement('<div directives><span viewroot directives></span></div>');
|
||||||
|
var directives = [SomeDecoratorDirective];
|
||||||
|
var results = createPipeline(directives).process(el);
|
||||||
|
expect(results[1].inheritedProtoElementInjector.parent).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use a null parent if there is an intermediate viewRoot', () => {
|
||||||
|
var el = createElement('<div directives><span viewroot><a directives></a></span></div>');
|
||||||
|
var directives = [SomeDecoratorDirective];
|
||||||
|
var results = createPipeline(directives).process(el);
|
||||||
|
expect(results[2].inheritedProtoElementInjector.parent).toBe(null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use the ProtoElementInjector of the parent element as parent', () => {
|
describe("distanceToParentInjector", () => {
|
||||||
var el = createElement('<div directives><span><a directives></a></span></div>');
|
it("should be 0 for root elements", () => {
|
||||||
var directives = [SomeDecoratorDirective];
|
var el = createElement('<div directives></div>');
|
||||||
var results = createPipeline(directives).process(el);
|
var directives = [SomeDecoratorDirective];
|
||||||
expect(results[2].inheritedProtoElementInjector.parent).toBe(
|
var results = createPipeline(directives).process(el);
|
||||||
results[0].inheritedProtoElementInjector);
|
expect(results[0].inheritedProtoElementInjector.distanceToParent).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use a null parent for viewRoots', () => {
|
it("should be 1 when a parent element has an injector", () => {
|
||||||
var el = createElement('<div directives><span viewroot directives></span></div>');
|
var el = createElement('<div directives><span directives></span></div>');
|
||||||
var directives = [SomeDecoratorDirective];
|
var directives = [SomeDecoratorDirective];
|
||||||
var results = createPipeline(directives).process(el);
|
var results = createPipeline(directives).process(el);
|
||||||
expect(results[1].inheritedProtoElementInjector.parent).toBe(null);
|
expect(results[1].inheritedProtoElementInjector.distanceToParent).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use a null parent if there is an intermediate viewRoot', () => {
|
it("should add 1 for every element that does not have an injector", () => {
|
||||||
var el = createElement('<div directives><span viewroot><a directives></a></span></div>');
|
var el = createElement('<div directives><a><b><span directives></span></b></a></div>');
|
||||||
var directives = [SomeDecoratorDirective];
|
var directives = [SomeDecoratorDirective];
|
||||||
var results = createPipeline(directives).process(el);
|
var results = createPipeline(directives).process(el);
|
||||||
expect(results[2].inheritedProtoElementInjector.parent).toBe(null);
|
expect(results[3].inheritedProtoElementInjector.distanceToParent).toBe(3);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -117,8 +142,8 @@ class TestableProtoElementInjectorBuilder extends ProtoElementInjectorBuilder {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
internalCreateProtoElementInjector(parent, index, bindings, firstBindingIsComponent) {
|
internalCreateProtoElementInjector(parent, index, bindings, firstBindingIsComponent, distance) {
|
||||||
var result = new ProtoElementInjector(parent, index, bindings, firstBindingIsComponent);
|
var result = new ProtoElementInjector(parent, index, bindings, firstBindingIsComponent, distance);
|
||||||
ListWrapper.push(this.debugObjects, result);
|
ListWrapper.push(this.debugObjects, result);
|
||||||
ListWrapper.push(this.debugObjects, {'parent': parent, 'index': index, 'bindings': bindings, 'firstBindingIsComponent': firstBindingIsComponent});
|
ListWrapper.push(this.debugObjects, {'parent': parent, 'index': index, 'bindings': bindings, 'firstBindingIsComponent': firstBindingIsComponent});
|
||||||
return result;
|
return result;
|
||||||
|
|
Loading…
Reference in New Issue