Skip to content

Commit c899b0a

Browse files
committed
feat(element_injector): support multiple injectables with the same token
1 parent 5ba5da5 commit c899b0a

File tree

6 files changed

+92
-43
lines changed

6 files changed

+92
-43
lines changed

modules/angular2/src/core/compiler/element_injector.ts

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -418,14 +418,8 @@ export class ProtoElementInjector {
418418
private static _createHostInjectorBindingData(dirBindings: List<ResolvedBinding>,
419419
bd: List<BindingData>,
420420
firstBindingIsComponent: boolean) {
421-
var visitedIds: Map<number, boolean> = new Map();
422421
ListWrapper.forEach(dirBindings, dirBinding => {
423422
ListWrapper.forEach(dirBinding.resolvedHostInjectables, b => {
424-
if (visitedIds.has(b.key.id)) {
425-
throw new BaseException(
426-
`Multiple directives defined the same host injectable: "${stringify(b.key.token)}"`);
427-
}
428-
visitedIds.set(b.key.id, true);
429423
bd.push(ProtoElementInjector._createBindingData(firstBindingIsComponent, dirBinding,
430424
dirBindings,
431425
ProtoElementInjector._createBinding(b)));
@@ -948,6 +942,10 @@ export class ElementInjector extends TreeNode<ElementInjector> {
948942
}
949943
}
950944

945+
addDirectivesMatchingQuery(query: Query, list: any[]): void {
946+
this._strategy.addDirectivesMatchingQuery(query, list);
947+
}
948+
951949
private _buildQueries(): void {
952950
if (isPresent(this._proto)) {
953951
this._strategy.buildQueries();
@@ -1158,6 +1156,7 @@ interface _ElementInjectorStrategy {
11581156
getComponent(): any;
11591157
isComponentKey(key: Key): boolean;
11601158
buildQueries(): void;
1159+
addDirectivesMatchingQuery(q: Query, res: any[]): void;
11611160
getObjByKeyId(keyId: number, visibility: number): any;
11621161
getDirectiveAtIndex(index: number): any;
11631162
getComponentBinding(): DirectiveBinding;
@@ -1234,17 +1233,18 @@ class ElementInjectorInlineStrategy implements _ElementInjectorStrategy {
12341233

12351234
hydrate(): void {
12361235
var p = this._protoStrategy;
1236+
var e = this._ei;
12371237

1238-
if (isPresent(p._keyId0)) this.getObjByKeyId(p._keyId0, LIGHT_DOM_AND_SHADOW_DOM);
1239-
if (isPresent(p._keyId1)) this.getObjByKeyId(p._keyId1, LIGHT_DOM_AND_SHADOW_DOM);
1240-
if (isPresent(p._keyId2)) this.getObjByKeyId(p._keyId2, LIGHT_DOM_AND_SHADOW_DOM);
1241-
if (isPresent(p._keyId3)) this.getObjByKeyId(p._keyId3, LIGHT_DOM_AND_SHADOW_DOM);
1242-
if (isPresent(p._keyId4)) this.getObjByKeyId(p._keyId4, LIGHT_DOM_AND_SHADOW_DOM);
1243-
if (isPresent(p._keyId5)) this.getObjByKeyId(p._keyId5, LIGHT_DOM_AND_SHADOW_DOM);
1244-
if (isPresent(p._keyId6)) this.getObjByKeyId(p._keyId6, LIGHT_DOM_AND_SHADOW_DOM);
1245-
if (isPresent(p._keyId7)) this.getObjByKeyId(p._keyId7, LIGHT_DOM_AND_SHADOW_DOM);
1246-
if (isPresent(p._keyId8)) this.getObjByKeyId(p._keyId8, LIGHT_DOM_AND_SHADOW_DOM);
1247-
if (isPresent(p._keyId9)) this.getObjByKeyId(p._keyId9, LIGHT_DOM_AND_SHADOW_DOM);
1238+
if (isPresent(p._keyId0) && isBlank(this._obj0)) this._obj0 = e._new(p._binding0);
1239+
if (isPresent(p._keyId1) && isBlank(this._obj1)) this._obj1 = e._new(p._binding1);
1240+
if (isPresent(p._keyId2) && isBlank(this._obj2)) this._obj2 = e._new(p._binding2);
1241+
if (isPresent(p._keyId3) && isBlank(this._obj3)) this._obj3 = e._new(p._binding3);
1242+
if (isPresent(p._keyId4) && isBlank(this._obj4)) this._obj4 = e._new(p._binding4);
1243+
if (isPresent(p._keyId5) && isBlank(this._obj5)) this._obj5 = e._new(p._binding5);
1244+
if (isPresent(p._keyId6) && isBlank(this._obj6)) this._obj6 = e._new(p._binding6);
1245+
if (isPresent(p._keyId7) && isBlank(this._obj7)) this._obj7 = e._new(p._binding7);
1246+
if (isPresent(p._keyId8) && isBlank(this._obj8)) this._obj8 = e._new(p._binding8);
1247+
if (isPresent(p._keyId9) && isBlank(this._obj9)) this._obj9 = e._new(p._binding9);
12481248
}
12491249

12501250
getComponent(): any { return this._obj0; }
@@ -1288,6 +1288,20 @@ class ElementInjectorInlineStrategy implements _ElementInjectorStrategy {
12881288
}
12891289
}
12901290

1291+
addDirectivesMatchingQuery(query: Query, list: any[]): void {
1292+
var p = this._protoStrategy;
1293+
if (isPresent(p._binding0) && p._binding0.key.token === query.selector) list.push(this._obj0);
1294+
if (isPresent(p._binding1) && p._binding1.key.token === query.selector) list.push(this._obj1);
1295+
if (isPresent(p._binding2) && p._binding2.key.token === query.selector) list.push(this._obj2);
1296+
if (isPresent(p._binding3) && p._binding3.key.token === query.selector) list.push(this._obj3);
1297+
if (isPresent(p._binding4) && p._binding4.key.token === query.selector) list.push(this._obj4);
1298+
if (isPresent(p._binding5) && p._binding5.key.token === query.selector) list.push(this._obj5);
1299+
if (isPresent(p._binding6) && p._binding6.key.token === query.selector) list.push(this._obj6);
1300+
if (isPresent(p._binding7) && p._binding7.key.token === query.selector) list.push(this._obj7);
1301+
if (isPresent(p._binding8) && p._binding8.key.token === query.selector) list.push(this._obj8);
1302+
if (isPresent(p._binding9) && p._binding9.key.token === query.selector) list.push(this._obj9);
1303+
}
1304+
12911305
getObjByKeyId(keyId: number, visibility: number): any {
12921306
var p = this._protoStrategy;
12931307

@@ -1406,8 +1420,8 @@ class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy {
14061420
var p = this._protoStrategy;
14071421

14081422
for (var i = 0; i < p._keyIds.length; i++) {
1409-
if (isPresent(p._keyIds[i])) {
1410-
this.getObjByKeyId(p._keyIds[i], LIGHT_DOM_AND_SHADOW_DOM);
1423+
if (isPresent(p._keyIds[i]) && isBlank(this._objs[i])) {
1424+
this._objs[i] = this._ei._new(p._bindings[i]);
14111425
}
14121426
}
14131427
}
@@ -1429,6 +1443,15 @@ class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy {
14291443
}
14301444
}
14311445

1446+
addDirectivesMatchingQuery(query: Query, list: any[]): void {
1447+
var p = this._protoStrategy;
1448+
1449+
for (var i = 0; i < p._bindings.length; i++) {
1450+
if (p._bindings[i].key.token === query.selector) list.push(this._objs[i]);
1451+
}
1452+
}
1453+
1454+
14321455
getObjByKeyId(keyId: number, visibility: number): any {
14331456
var p = this._protoStrategy;
14341457

@@ -1518,8 +1541,6 @@ class QueryRef {
15181541
}
15191542

15201543
private _aggregateDirective(inj: ElementInjector, aggregator: List<any>): void {
1521-
if (inj.hasDirective(this.query.selector)) {
1522-
aggregator.push(inj.get(this.query.selector));
1523-
}
1544+
inj.addDirectivesMatchingQuery(this.query, aggregator);
15241545
}
15251546
}

modules/angular2/src/facade/collection.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ class ListWrapper {
184184

185185
bool isListLikeIterable(obj) => obj is Iterable;
186186

187-
List<T> iterableToList(Iterable<T> ii) => ii.toList();
187+
List iterableToList(Iterable ii) => ii.toList();
188188

189189
void iterateListLike(iter, fn(item)) {
190190
assert(iter is Iterable);

modules/angular2/src/facade/collection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ export function iterateListLike(obj, fn: Function) {
253253
}
254254
}
255255
}
256-
export function iterableToList<T>(ii:Iterable<T>):List<T> {
256+
export function iterableToList<T>(ii: any): List<T> {
257257
var res = [];
258258
for (var i of ii) {
259259
res.push(i);

modules/angular2/src/forms/directives/shared.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ export function setUpControl(c: Control, dir: NgControl) {
3838

3939
export function composeNgValidator(ngValidators: QueryList<NgValidator>): Function {
4040
if (isBlank(ngValidators)) return Validators.nullValidator;
41-
return Validators.compose(iterableToList(ngValidators).map(v => v.validator));
41+
return Validators.compose(
42+
(<List<NgValidator>>iterableToList(ngValidators)).map(v => v.validator));
4243
}
4344

4445
function _throwError(dir: NgControl, message: string): void {
@@ -49,5 +50,5 @@ function _throwError(dir: NgControl, message: string): void {
4950
export function setProperty(renderer: Renderer, elementRef: ElementRef, propName: string,
5051
propValue: any) {
5152
renderer.setElementProperty(elementRef.parentView.render, elementRef.boundElementIndex, propName,
52-
propValue);
53+
propValue);
5354
}

modules/angular2/test/core/compiler/element_injector_spec.ts

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -486,20 +486,6 @@ export function main() {
486486
expect(pei.getBindingAtIndex(i).key.token).toBe(i);
487487
}
488488
});
489-
490-
it('should throw whenever multiple directives declare the same host injectable', () => {
491-
expect(() => {
492-
createPei(null, 0, [
493-
DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component({
494-
hostInjector: [bind('injectable1').toValue('injectable1')]
495-
})),
496-
DirectiveBinding.createFromType(SomeOtherDirective, new dirAnn.Component({
497-
hostInjector: [bind('injectable1').toValue('injectable2')]
498-
}))
499-
]);
500-
}).toThrowError('Multiple directives defined the same host injectable: "injectable1"');
501-
});
502-
503489
});
504490
});
505491

@@ -968,12 +954,47 @@ export function main() {
968954
});
969955

970956
it('should contain directives on the same injector', () => {
971-
var inj = injector(ListWrapper.concat([NeedsQuery, CountingDirective], extraBindings), null,
957+
var inj = injector(ListWrapper.concat([
958+
NeedsQuery,
959+
CountingDirective
960+
], extraBindings), null,
972961
false, preBuildObjects);
973962

974963
expectDirectives(inj.get(NeedsQuery).query, CountingDirective, [0]);
975964
})
976965

966+
it('should contain multiple directives from the same injector', () => {
967+
var inj = injector(ListWrapper.concat([
968+
NeedsQuery,
969+
CountingDirective,
970+
FancyCountingDirective,
971+
bind(CountingDirective).toAlias(FancyCountingDirective)
972+
], extraBindings), null,
973+
false, preBuildObjects);
974+
975+
expect(inj.get(NeedsQuery).query.length).toEqual(2);
976+
expect(inj.get(NeedsQuery).query.first).toBeAnInstanceOf(CountingDirective);
977+
expect(inj.get(NeedsQuery).query.last).toBeAnInstanceOf(FancyCountingDirective);
978+
})
979+
980+
it('should contain multiple directives from the same injector after linking', () => {
981+
var inj = parentChildInjectors([], ListWrapper.concat([
982+
NeedsQuery,
983+
CountingDirective,
984+
FancyCountingDirective,
985+
bind(CountingDirective).toAlias(FancyCountingDirective)
986+
], extraBindings));
987+
988+
var parent = inj.parent;
989+
990+
inj.unlink();
991+
inj.link(parent);
992+
993+
expect(inj.get(NeedsQuery).query.length).toEqual(2);
994+
expect(inj.get(NeedsQuery).query.first).toBeAnInstanceOf(CountingDirective);
995+
expect(inj.get(NeedsQuery).query.last).toBeAnInstanceOf(FancyCountingDirective);
996+
})
997+
977998
it('should contain the element when no directives are bound to the var binding', () => {
978999
var dirs = [NeedsQueryByVarBindings];
9791000

modules/examples/src/template_driven_forms/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ import {
66
Component,
77
Directive,
88
View,
9-
Ancestor
9+
Ancestor,
10+
NgValidator,
11+
forwardRef,
12+
Binding
1013
} from 'angular2/angular2';
1114
import {formDirectives, NgControl, Validators, NgForm} from 'angular2/forms';
1215

13-
import {RegExpWrapper, print, isPresent} from 'angular2/src/facade/lang';
16+
import {RegExpWrapper, print, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
1417

1518
import {reflector} from 'angular2/src/reflection/reflection';
1619
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
@@ -33,7 +36,10 @@ class CheckoutModel {
3336
/**
3437
* Custom validator.
3538
*/
36-
@Directive({selector: '[credit-card]'})
39+
const creditCardValidatorBinding =
40+
CONST_EXPR(new Binding(NgValidator, {toAlias: forwardRef(() => CreditCardValidator)}));
41+
42+
@Directive({selector: '[credit-card]', hostInjector: [creditCardValidatorBinding]})
3743
class CreditCardValidator {
3844
get validator() { return CreditCardValidator.validate; }
3945

0 commit comments

Comments
 (0)