Skip to content

Commit 3c692a1

Browse files
committed
feat(element_injector): add distance to propertly implement @parent
1 parent bed4b52 commit 3c692a1

File tree

6 files changed

+106
-41
lines changed

6 files changed

+106
-41
lines changed

modules/core/src/compiler/element_injector.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,11 @@ export class ProtoElementInjector {
165165
parent:ProtoElementInjector;
166166
index:int;
167167
view:View;
168-
constructor(parent:ProtoElementInjector, index:int, bindings:List, firstBindingIsComponent:boolean = false) {
168+
distanceToParent:number;
169+
constructor(parent:ProtoElementInjector, index:int, bindings:List, firstBindingIsComponent:boolean = false, distanceToParent:number = 0) {
169170
this.parent = parent;
170171
this.index = index;
172+
this.distanceToParent = distanceToParent;
171173

172174
this._binding0IsComponent = firstBindingIsComponent;
173175
this._binding0 = null; this._keyId0 = null;
@@ -331,6 +333,10 @@ export class ElementInjector extends TreeNode {
331333
}
332334
}
333335

336+
directParent(): ElementInjector {
337+
return this._proto.distanceToParent < 2 ? this.parent : null;
338+
}
339+
334340
_isComponentKey(key:Key) {
335341
return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
336342
}
@@ -396,11 +402,11 @@ export class ElementInjector extends TreeNode {
396402
*
397403
* Write benchmarks before doing this optimization.
398404
*/
399-
_getByKey(key:Key, depth:int, requestor:Key) {
405+
_getByKey(key:Key, depth:number, requestor:Key) {
400406
var ei = this;
401407

402408
if (! this._shouldIncludeSelf(depth)) {
403-
depth -= 1;
409+
depth -= ei._proto.distanceToParent;
404410
ei = ei._parent;
405411
}
406412

@@ -411,8 +417,8 @@ export class ElementInjector extends TreeNode {
411417
var dir = ei._getDirectiveByKeyId(key.id);
412418
if (dir !== _undefined) return dir;
413419

420+
depth -= ei._proto.distanceToParent;
414421
ei = ei._parent;
415-
depth -= 1;
416422
}
417423

418424
if (isPresent(this._host) && this._host._isComponentKey(key)) {
@@ -440,7 +446,7 @@ export class ElementInjector extends TreeNode {
440446
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
441447
if (keyId === staticKeys.viewPortId) return this._preBuiltObjects.viewPort;
442448
if (keyId === staticKeys.destinationLightDomId) {
443-
var p:ElementInjector = this._parent;
449+
var p:ElementInjector = this.directParent();
444450
return isPresent(p) ? p._preBuiltObjects.lightDom : null;
445451
}
446452
if (keyId === staticKeys.sourceLightDomId) {

modules/core/src/compiler/pipeline/compile_element.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export class CompileElement {
3232
inheritedProtoView:ProtoView;
3333
inheritedProtoElementInjector:ProtoElementInjector;
3434
inheritedElementBinder:ElementBinder;
35+
distanceToParentInjector:number;
3536
compileChildren: boolean;
3637
constructor(element:Element) {
3738
this.element = element;
@@ -55,6 +56,7 @@ export class CompileElement {
5556
// inherited down to children if they don't have
5657
// an own elementBinder
5758
this.inheritedElementBinder = null;
59+
this.distanceToParentInjector = 0;
5860
this.compileChildren = true;
5961
}
6062

modules/core/src/compiler/pipeline/proto_element_injector_builder.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,37 @@ import {CompileControl} from './compile_control';
2424
*/
2525
export class ProtoElementInjectorBuilder extends CompileStep {
2626
// public so that we can overwrite it in tests
27-
internalCreateProtoElementInjector(parent, index, directives, firstBindingIsComponent) {
28-
return new ProtoElementInjector(parent, index, directives, firstBindingIsComponent);
27+
internalCreateProtoElementInjector(parent, index, directives, firstBindingIsComponent, distance) {
28+
return new ProtoElementInjector(parent, index, directives, firstBindingIsComponent, distance);
2929
}
3030

3131
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
32-
var inheritedProtoElementInjector = null;
32+
var distanceToParentInjector = this._getDistanceToParentInjector(parent, current);
3333
var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current);
3434
var injectorBindings = this._collectDirectiveBindings(current);
35+
3536
// TODO: add lightDomServices as well,
3637
// but after the directives as we rely on that order
3738
// in the element_binder_builder.
3839

3940
if (injectorBindings.length > 0) {
4041
var protoView = current.inheritedProtoView;
4142
var hasComponent = isPresent(current.componentDirective);
42-
inheritedProtoElementInjector = this.internalCreateProtoElementInjector(
43-
parentProtoElementInjector, protoView.elementBinders.length, injectorBindings, hasComponent
43+
44+
current.inheritedProtoElementInjector = this.internalCreateProtoElementInjector(
45+
parentProtoElementInjector, protoView.elementBinders.length, injectorBindings,
46+
hasComponent, distanceToParentInjector
4447
);
48+
current.distanceToParentInjector = 0;
49+
4550
} else {
46-
inheritedProtoElementInjector = parentProtoElementInjector;
51+
current.inheritedProtoElementInjector = parentProtoElementInjector;
52+
current.distanceToParentInjector = distanceToParentInjector;
4753
}
48-
current.inheritedProtoElementInjector = inheritedProtoElementInjector;
54+
}
55+
56+
_getDistanceToParentInjector(parent, current) {
57+
return isPresent(parent) ? parent.distanceToParentInjector + 1 : 0;
4958
}
5059

5160
_getParentProtoElementInjector(parent, current) {

modules/core/test/compiler/element_injector_spec.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export function main() {
103103

104104
parent.instantiateDirectives(inj, null, parentPreBuildObjects);
105105

106-
var protoChild = new ProtoElementInjector(protoParent, 1, childBindings);
106+
var protoChild = new ProtoElementInjector(protoParent, 1, childBindings, false, 1);
107107
var child = protoChild.instantiate(parent, null);
108108
child.instantiateDirectives(inj, null, defaultPreBuiltObjects);
109109

@@ -120,7 +120,7 @@ export function main() {
120120
var host = protoParent.instantiate(null, null);
121121
host.instantiateDirectives(inj, shadowInj, hostPreBuildObjects);
122122

123-
var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false);
123+
var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false, 1);
124124
var shadow = protoChild.instantiate(null, host);
125125
shadow.instantiateDirectives(shadowInj, null, null);
126126

@@ -144,6 +144,30 @@ export function main() {
144144
[c2, 'child2']
145145
])).toEqual(["parent", ["child1", "child2"]]);
146146
});
147+
148+
describe("direct parent", () => {
149+
it("should return parent injector when distance is 1", () => {
150+
var distance = 1;
151+
var protoParent = new ProtoElementInjector(null, 0, []);
152+
var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
153+
154+
var p = protoParent.instantiate(null, null);
155+
var c = protoChild.instantiate(p, null);
156+
157+
expect(c.directParent()).toEqual(p);
158+
});
159+
160+
it("should return null otherwise", () => {
161+
var distance = 2;
162+
var protoParent = new ProtoElementInjector(null, 0, []);
163+
var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
164+
165+
var p = protoParent.instantiate(null, null);
166+
var c = protoChild.instantiate(p, null);
167+
168+
expect(c.directParent()).toEqual(null);
169+
});
170+
});
147171
});
148172

149173
describe("hasBindings", function () {

modules/core/test/compiler/integration_spec.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ export function main() {
128128
expect(DOM.getText(view.nodes[0])).toEqual('Before LightDOM After');
129129
done();
130130
});
131-
132131
});
133132
});
134133
}

modules/core/test/compiler/pipeline/proto_element_injector_builder_spec.js

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -73,32 +73,57 @@ export function main() {
7373
expect(creationArgs['index']).toBe(protoView.elementBinders.length);
7474
});
7575

76-
it('should inherit the ProtoElementInjector down to children without directives', () => {
77-
var directives = [SomeDecoratorDirective];
78-
var results = createPipeline(directives).process(createElement('<div directives><span></span></div>'));
79-
expect(results[1].inheritedProtoElementInjector).toBe(results[0].inheritedProtoElementInjector);
80-
});
81-
82-
it('should use the ProtoElementInjector of the parent element as parent', () => {
83-
var el = createElement('<div directives><span><a directives></a></span></div>');
84-
var directives = [SomeDecoratorDirective];
85-
var results = createPipeline(directives).process(el);
86-
expect(results[2].inheritedProtoElementInjector.parent).toBe(
87-
results[0].inheritedProtoElementInjector);
76+
describe("inheritedProtoElementInjector", () => {
77+
it('should inherit the ProtoElementInjector down to children without directives', () => {
78+
var directives = [SomeDecoratorDirective];
79+
var results = createPipeline(directives).process(createElement('<div directives><span></span></div>'));
80+
expect(results[1].inheritedProtoElementInjector).toBe(results[0].inheritedProtoElementInjector);
81+
});
82+
83+
it('should use the ProtoElementInjector of the parent element as parent', () => {
84+
var el = createElement('<div directives><span><a directives></a></span></div>');
85+
var directives = [SomeDecoratorDirective];
86+
var results = createPipeline(directives).process(el);
87+
expect(results[2].inheritedProtoElementInjector.parent).toBe(
88+
results[0].inheritedProtoElementInjector);
89+
});
90+
91+
it('should use a null parent for viewRoots', () => {
92+
var el = createElement('<div directives><span viewroot directives></span></div>');
93+
var directives = [SomeDecoratorDirective];
94+
var results = createPipeline(directives).process(el);
95+
expect(results[1].inheritedProtoElementInjector.parent).toBe(null);
96+
});
97+
98+
it('should use a null parent if there is an intermediate viewRoot', () => {
99+
var el = createElement('<div directives><span viewroot><a directives></a></span></div>');
100+
var directives = [SomeDecoratorDirective];
101+
var results = createPipeline(directives).process(el);
102+
expect(results[2].inheritedProtoElementInjector.parent).toBe(null);
103+
});
88104
});
89105

90-
it('should use a null parent for viewRoots', () => {
91-
var el = createElement('<div directives><span viewroot directives></span></div>');
92-
var directives = [SomeDecoratorDirective];
93-
var results = createPipeline(directives).process(el);
94-
expect(results[1].inheritedProtoElementInjector.parent).toBe(null);
95-
});
96-
97-
it('should use a null parent if there is an intermediate viewRoot', () => {
98-
var el = createElement('<div directives><span viewroot><a directives></a></span></div>');
99-
var directives = [SomeDecoratorDirective];
100-
var results = createPipeline(directives).process(el);
101-
expect(results[2].inheritedProtoElementInjector.parent).toBe(null);
106+
describe("distanceToParentInjector", () => {
107+
it("should be 0 for root elements", () => {
108+
var el = createElement('<div directives></div>');
109+
var directives = [SomeDecoratorDirective];
110+
var results = createPipeline(directives).process(el);
111+
expect(results[0].inheritedProtoElementInjector.distanceToParent).toBe(0);
112+
});
113+
114+
it("should be 1 when a parent element has an injector", () => {
115+
var el = createElement('<div directives><span directives></span></div>');
116+
var directives = [SomeDecoratorDirective];
117+
var results = createPipeline(directives).process(el);
118+
expect(results[1].inheritedProtoElementInjector.distanceToParent).toBe(1);
119+
});
120+
121+
it("should add 1 for every element that does not have an injector", () => {
122+
var el = createElement('<div directives><a><b><span directives></span></b></a></div>');
123+
var directives = [SomeDecoratorDirective];
124+
var results = createPipeline(directives).process(el);
125+
expect(results[3].inheritedProtoElementInjector.distanceToParent).toBe(3);
126+
});
102127
});
103128
});
104129
}
@@ -117,8 +142,8 @@ class TestableProtoElementInjectorBuilder extends ProtoElementInjectorBuilder {
117142
}
118143
return null;
119144
}
120-
internalCreateProtoElementInjector(parent, index, bindings, firstBindingIsComponent) {
121-
var result = new ProtoElementInjector(parent, index, bindings, firstBindingIsComponent);
145+
internalCreateProtoElementInjector(parent, index, bindings, firstBindingIsComponent, distance) {
146+
var result = new ProtoElementInjector(parent, index, bindings, firstBindingIsComponent, distance);
122147
ListWrapper.push(this.debugObjects, result);
123148
ListWrapper.push(this.debugObjects, {'parent': parent, 'index': index, 'bindings': bindings, 'firstBindingIsComponent': firstBindingIsComponent});
124149
return result;

0 commit comments

Comments
 (0)