Skip to content

Commit 7308a3a

Browse files
committed
refactor(ElementInjector): support components
- Allow to access containing component directive instance from the shadow DOM. - Allow to access app services of the app level injector of the component when the component is instantiated.
1 parent c68e780 commit 7308a3a

File tree

8 files changed

+207
-95
lines changed

8 files changed

+207
-95
lines changed

modules/benchmarks/src/element_injector/instantiate_benchmark.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ export function run () {
1010
var bindings = [A, B, C];
1111
var proto = new ProtoElementInjector(null, 0, bindings);
1212
for (var i = 0; i < ITERATIONS; ++i) {
13-
var ei = proto.instantiate(null,null);
14-
ei.instantiateDirectives(appInjector);
13+
var ei = proto.instantiate(null,null,null);
14+
ei.instantiateDirectives(appInjector, null);
1515
}
1616
}
1717

modules/benchmarks/src/element_injector/instantiate_benchmark_codegen.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ var count = 0;
66

77
export function run () {
88
var appInjector = new Injector([]);
9-
9+
1010
var bindings = [
1111
new Binding(Key.get(A), () => new A(), [], false),
1212
new Binding(Key.get(B), () => new B(), [], false),
@@ -18,8 +18,8 @@ export function run () {
1818

1919
var proto = new ProtoElementInjector(null, 0, bindings);
2020
for (var i = 0; i < ITERATIONS; ++i) {
21-
var ei = proto.instantiate(null,null);
22-
ei.instantiateDirectives(appInjector);
21+
var ei = proto.instantiate(null,null,null);
22+
ei.instantiateDirectives(appInjector, null);
2323
}
2424
}
2525

modules/benchmarks/src/element_injector/instantiate_directive_benchmark.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ export function run () {
99

1010
var bindings = [A, B, C];
1111
var proto = new ProtoElementInjector(null, 0, bindings);
12-
var ei = proto.instantiate(null,null);
12+
var ei = proto.instantiate(null,null,null);
1313

1414
for (var i = 0; i < ITERATIONS; ++i) {
1515
ei.clearDirectives();
16-
ei.instantiateDirectives(appInjector);
16+
ei.instantiateDirectives(appInjector, null);
1717
}
1818
}
1919

modules/core/src/compiler/element_injector.js

Lines changed: 80 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {FIELD, isPresent, isBlank, Type, int} from 'facade/lang';
1+
import {FIELD, isPresent, isBlank, Type, int, BaseException} from 'facade/lang';
22
import {Math} from 'facade/math';
33
import {List, ListWrapper} from 'facade/collection';
44
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError, CyclicDependencyError} from 'di/di';
@@ -106,6 +106,7 @@ export class ProtoElementInjector {
106106
@FIELD('_binding7:Binding')
107107
@FIELD('_binding8:Binding')
108108
@FIELD('_binding9:Binding')
109+
@FIELD('_binding0IsComponent:int')
109110
@FIELD('_key0:int')
110111
@FIELD('_key1:int')
111112
@FIELD('_key2:int')
@@ -118,10 +119,11 @@ export class ProtoElementInjector {
118119
@FIELD('_key9:int')
119120
@FIELD('final parent:ProtoElementInjector')
120121
@FIELD('final index:int')
121-
constructor(parent:ProtoElementInjector, index:int, bindings:List) {
122+
constructor(parent:ProtoElementInjector, index:int, bindings:List, firstBindingIsComponent:boolean = false) {
122123
this.parent = parent;
123124
this.index = index;
124125

126+
this._binding0IsComponent = firstBindingIsComponent;
125127
this._binding0 = null; this._keyId0 = null;
126128
this._binding1 = null; this._keyId1 = null;
127129
this._binding2 = null; this._keyId2 = null;
@@ -150,8 +152,8 @@ export class ProtoElementInjector {
150152
}
151153
}
152154

153-
instantiate(parent:ElementInjector, view):ElementInjector {
154-
return new ElementInjector(this, parent, view);
155+
instantiate(parent:ElementInjector, host:ElementInjector, view):ElementInjector {
156+
return new ElementInjector(this, parent, host, view);
155157
}
156158

157159
_createBinding(bindingOrType) {
@@ -212,7 +214,9 @@ export class ElementInjector extends TreeNode {
212214
*/
213215

214216
@FIELD('_proto:ProtoElementInjector')
215-
@FIELD('_appInjector:Injector')
217+
@FIELD('_lightDomAppInjector:Injector')
218+
@FIELD('_shadowDomAppInjector:Injector')
219+
@FIELD('_host:ElementInjector')
216220
@FIELD('_obj0:Object')
217221
@FIELD('_obj1:Object')
218222
@FIELD('_obj2:Object')
@@ -224,13 +228,24 @@ export class ElementInjector extends TreeNode {
224228
@FIELD('_obj8:Object')
225229
@FIELD('_obj9:Object')
226230
@FIELD('_view:View')
227-
constructor(proto:ProtoElementInjector, parent:ElementInjector, view) {
231+
constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector, view) {
228232
super(parent);
233+
if (isPresent(parent) && isPresent(host)) {
234+
throw new BaseException('Only either parent or host is allowed');
235+
}
236+
this._host = null; // needed to satisfy Dart
237+
if (isPresent(parent)) {
238+
this._host = parent._host;
239+
} else {
240+
this._host = host;
241+
}
242+
229243
this._proto = proto;
230244
this._view = view;
231245

232246
//we cannot call clearDirectives because fields won't be detected
233-
this._appInjector = null;
247+
this._lightDomAppInjector = null;
248+
this._shadowDomAppInjector = null;
234249
this._obj0 = null;
235250
this._obj1 = null;
236251
this._obj2 = null;
@@ -245,7 +260,9 @@ export class ElementInjector extends TreeNode {
245260
}
246261

247262
clearDirectives() {
248-
this._appInjector = null;
263+
this._lightDomAppInjector = null;
264+
this._shadowDomAppInjector = null;
265+
249266
this._obj0 = null;
250267
this._obj1 = null;
251268
this._obj2 = null;
@@ -259,24 +276,42 @@ export class ElementInjector extends TreeNode {
259276
this._constructionCounter = 0;
260277
}
261278

262-
instantiateDirectives(appInjector:Injector) {
263-
this._appInjector = appInjector;
264-
279+
instantiateDirectives(lightDomAppInjector:Injector, shadowDomAppInjector:Injector) {
265280
var p = this._proto;
281+
if (this._proto._binding0IsComponent && isBlank(shadowDomAppInjector)) {
282+
throw new BaseException('A shadowDomAppInjector is required as this ElementInjector contains a component');
283+
} else if (!this._proto._binding0IsComponent && isPresent(shadowDomAppInjector)) {
284+
throw new BaseException('No shadowDomAppInjector allowed as there is not component stored in this ElementInjector');
285+
}
286+
this._lightDomAppInjector = lightDomAppInjector;
287+
this._shadowDomAppInjector = shadowDomAppInjector;
288+
266289
if (isPresent(p._keyId0)) this._getDirectiveByKeyId(p._keyId0);
267290
if (isPresent(p._keyId1)) this._getDirectiveByKeyId(p._keyId1);
268-
if (isPresent(p._keyId2)) this._getDirectiveByKeyId(p._keyId2);;
269-
if (isPresent(p._keyId3)) this._getDirectiveByKeyId(p._keyId3);;
270-
if (isPresent(p._keyId4)) this._getDirectiveByKeyId(p._keyId4);;
271-
if (isPresent(p._keyId5)) this._getDirectiveByKeyId(p._keyId5);;
272-
if (isPresent(p._keyId6)) this._getDirectiveByKeyId(p._keyId6);;
273-
if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7);;
274-
if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8);;
275-
if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9);;
291+
if (isPresent(p._keyId2)) this._getDirectiveByKeyId(p._keyId2);
292+
if (isPresent(p._keyId3)) this._getDirectiveByKeyId(p._keyId3);
293+
if (isPresent(p._keyId4)) this._getDirectiveByKeyId(p._keyId4);
294+
if (isPresent(p._keyId5)) this._getDirectiveByKeyId(p._keyId5);
295+
if (isPresent(p._keyId6)) this._getDirectiveByKeyId(p._keyId6);
296+
if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7);
297+
if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8);
298+
if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9);
276299
}
277300

278301
get(token) {
279-
return this._getByKey(Key.get(token), 0);
302+
return this._getByKey(Key.get(token), 0, null);
303+
}
304+
305+
getComponent() {
306+
if (this._proto._binding0IsComponent) {
307+
return this._obj0;
308+
} else {
309+
throw new BaseException('There is not component stored in this ElementInjector');
310+
}
311+
}
312+
313+
_isComponentKey(key:Key) {
314+
return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
280315
}
281316

282317
_new(binding:Binding) {
@@ -290,16 +325,16 @@ export class ElementInjector extends TreeNode {
290325

291326
var d0,d1,d2,d3,d4,d5,d6,d7,d8,d9;
292327
try {
293-
d0 = length > 0 ? this._getByDependency(deps[0]) : null;
294-
d1 = length > 1 ? this._getByDependency(deps[1]) : null;
295-
d2 = length > 2 ? this._getByDependency(deps[2]) : null;
296-
d3 = length > 3 ? this._getByDependency(deps[3]) : null;
297-
d4 = length > 4 ? this._getByDependency(deps[4]) : null;
298-
d5 = length > 5 ? this._getByDependency(deps[5]) : null;
299-
d6 = length > 6 ? this._getByDependency(deps[6]) : null;
300-
d7 = length > 7 ? this._getByDependency(deps[7]) : null;
301-
d8 = length > 8 ? this._getByDependency(deps[8]) : null;
302-
d9 = length > 9 ? this._getByDependency(deps[9]) : null;
328+
d0 = length > 0 ? this._getByDependency(deps[0], binding.key) : null;
329+
d1 = length > 1 ? this._getByDependency(deps[1], binding.key) : null;
330+
d2 = length > 2 ? this._getByDependency(deps[2], binding.key) : null;
331+
d3 = length > 3 ? this._getByDependency(deps[3], binding.key) : null;
332+
d4 = length > 4 ? this._getByDependency(deps[4], binding.key) : null;
333+
d5 = length > 5 ? this._getByDependency(deps[5], binding.key) : null;
334+
d6 = length > 6 ? this._getByDependency(deps[6], binding.key) : null;
335+
d7 = length > 7 ? this._getByDependency(deps[7], binding.key) : null;
336+
d8 = length > 8 ? this._getByDependency(deps[8], binding.key) : null;
337+
d9 = length > 9 ? this._getByDependency(deps[9], binding.key) : null;
303338
} catch(e) {
304339
if (e instanceof ProviderError) e.addKey(binding.key);
305340
throw e;
@@ -324,8 +359,8 @@ export class ElementInjector extends TreeNode {
324359
return obj;
325360
}
326361

327-
_getByDependency(dep:DirectiveDependency) {
328-
return this._getByKey(dep.key, dep.depth);
362+
_getByDependency(dep:DirectiveDependency, requestor:Key) {
363+
return this._getByKey(dep.key, dep.depth, requestor);
329364
}
330365

331366
/*
@@ -340,7 +375,7 @@ export class ElementInjector extends TreeNode {
340375
*
341376
* Write benchmarks before doing this optimization.
342377
*/
343-
_getByKey(key:Key, depth:int) {
378+
_getByKey(key:Key, depth:int, requestor:Key) {
344379
var ei = this;
345380
while (ei != null && depth >= 0) {
346381
var specObj = ei._getSpecialObjectByKeyId(key.id);
@@ -352,7 +387,17 @@ export class ElementInjector extends TreeNode {
352387
ei = ei._parent;
353388
depth -= 1;
354389
}
355-
return this._appInjector.get(key);
390+
if (isPresent(this._host) && this._host._isComponentKey(key)) {
391+
return this._host.getComponent();
392+
} else {
393+
var appInjector;
394+
if (isPresent(requestor) && this._isComponentKey(requestor)) {
395+
appInjector = this._shadowDomAppInjector;
396+
} else {
397+
appInjector = this._lightDomAppInjector;
398+
}
399+
return appInjector.get(key);
400+
}
356401
}
357402

358403
_getSpecialObjectByKeyId(keyId:int) {
@@ -364,6 +409,7 @@ export class ElementInjector extends TreeNode {
364409

365410
_getDirectiveByKeyId(keyId:int) {
366411
var p = this._proto;
412+
367413
if (p._keyId0 === keyId) {if (isBlank(this._obj0)){this._obj0 = this._new(p._binding0);} return this._obj0;}
368414
if (p._keyId1 === keyId) {if (isBlank(this._obj1)){this._obj1 = this._new(p._binding1);} return this._obj1;}
369415
if (p._keyId2 === keyId) {if (isBlank(this._obj2)){this._obj2 = this._new(p._binding2);} return this._obj2;}

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

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import {isPresent,} from 'facade/lang';
1+
import {isPresent, isBlank} from 'facade/lang';
22
import {ListWrapper} from 'facade/collection';
33

4-
import {ProtoElementInjector} from '../element_injector';
4+
import {Key} from 'di/di';
5+
import {ProtoElementInjector, ComponentKeyMetaData} from '../element_injector';
56

67
import {CompileStep} from './compile_step';
78
import {CompileElement} from './compile_element';
@@ -23,22 +24,23 @@ import {CompileControl} from './compile_control';
2324
*/
2425
export class ProtoElementInjectorBuilder extends CompileStep {
2526
// public so that we can overwrite it in tests
26-
internalCreateProtoElementInjector(parent, index, directives) {
27-
return new ProtoElementInjector(parent, index, directives);
27+
internalCreateProtoElementInjector(parent, index, directives, firstBindingIsComponent) {
28+
return new ProtoElementInjector(parent, index, directives, firstBindingIsComponent);
2829
}
2930

3031
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
3132
var inheritedProtoElementInjector = null;
3233
var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current);
33-
var injectorBindings = this._collectDirectiveTypes(current);
34+
var injectorBindings = this._collectDirectiveBindings(current);
3435
// TODO: add lightDomServices as well,
3536
// but after the directives as we rely on that order
3637
// in the element_binder_builder.
3738

3839
if (injectorBindings.length > 0) {
3940
var protoView = current.inheritedProtoView;
41+
var hasComponent = isPresent(current.componentDirective);
4042
inheritedProtoElementInjector = this.internalCreateProtoElementInjector(
41-
parentProtoElementInjector, protoView.elementBinders.length, injectorBindings
43+
parentProtoElementInjector, protoView.elementBinders.length, injectorBindings, hasComponent
4244
);
4345
} else {
4446
inheritedProtoElementInjector = parentProtoElementInjector;
@@ -56,18 +58,18 @@ export class ProtoElementInjectorBuilder extends CompileStep {
5658
return parentProtoElementInjector;
5759
}
5860

59-
_collectDirectiveTypes(pipelineElement) {
61+
_collectDirectiveBindings(pipelineElement) {
6062
var directiveTypes = [];
61-
if (isPresent(pipelineElement.decoratorDirectives)) {
62-
for (var i=0; i<pipelineElement.decoratorDirectives.length; i++) {
63-
ListWrapper.push(directiveTypes, pipelineElement.decoratorDirectives[i].type);
64-
}
63+
if (isPresent(pipelineElement.componentDirective)) {
64+
ListWrapper.push(directiveTypes, pipelineElement.componentDirective.type);
6565
}
6666
if (isPresent(pipelineElement.templateDirective)) {
6767
ListWrapper.push(directiveTypes, pipelineElement.templateDirective.type);
6868
}
69-
if (isPresent(pipelineElement.componentDirective)) {
70-
ListWrapper.push(directiveTypes, pipelineElement.componentDirective.type);
69+
if (isPresent(pipelineElement.decoratorDirectives)) {
70+
for (var i=0; i<pipelineElement.decoratorDirectives.length; i++) {
71+
ListWrapper.push(directiveTypes, pipelineElement.decoratorDirectives[i].type);
72+
}
7173
}
7274
return directiveTypes;
7375
}

modules/core/src/compiler/view.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,13 @@ export class ProtoView {
167167
static _instantiateDirectives(
168168
injectors:List<ElementInjectors>, appInjector:Injector) {
169169
for (var i = 0; i < injectors.length; ++i) {
170-
if (injectors[i] != null) injectors[i].instantiateDirectives(appInjector);
170+
if (injectors[i] != null) injectors[i].instantiateDirectives(appInjector, null);
171171
}
172172
}
173173

174174
static _createElementInjector(element, parent:ElementInjector, proto:ProtoElementInjector) {
175175
//TODO: vsavkin: pass element to `proto.instantiate()` once https://github.com/angular/angular/pull/98 is merged
176-
return proto.instantiate(parent, null);
176+
return proto.instantiate(parent, null, null);
177177
}
178178

179179
static _rootElementInjectors(injectors) {

0 commit comments

Comments
 (0)