feat(element_injector): support multiple injectables with the same token
This commit is contained in:
parent
5ba5da5d25
commit
c899b0a74c
|
@ -418,14 +418,8 @@ export class ProtoElementInjector {
|
||||||
private static _createHostInjectorBindingData(dirBindings: List<ResolvedBinding>,
|
private static _createHostInjectorBindingData(dirBindings: List<ResolvedBinding>,
|
||||||
bd: List<BindingData>,
|
bd: List<BindingData>,
|
||||||
firstBindingIsComponent: boolean) {
|
firstBindingIsComponent: boolean) {
|
||||||
var visitedIds: Map<number, boolean> = new Map();
|
|
||||||
ListWrapper.forEach(dirBindings, dirBinding => {
|
ListWrapper.forEach(dirBindings, dirBinding => {
|
||||||
ListWrapper.forEach(dirBinding.resolvedHostInjectables, b => {
|
ListWrapper.forEach(dirBinding.resolvedHostInjectables, b => {
|
||||||
if (visitedIds.has(b.key.id)) {
|
|
||||||
throw new BaseException(
|
|
||||||
`Multiple directives defined the same host injectable: "${stringify(b.key.token)}"`);
|
|
||||||
}
|
|
||||||
visitedIds.set(b.key.id, true);
|
|
||||||
bd.push(ProtoElementInjector._createBindingData(firstBindingIsComponent, dirBinding,
|
bd.push(ProtoElementInjector._createBindingData(firstBindingIsComponent, dirBinding,
|
||||||
dirBindings,
|
dirBindings,
|
||||||
ProtoElementInjector._createBinding(b)));
|
ProtoElementInjector._createBinding(b)));
|
||||||
|
@ -948,6 +942,10 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addDirectivesMatchingQuery(query: Query, list: any[]): void {
|
||||||
|
this._strategy.addDirectivesMatchingQuery(query, list);
|
||||||
|
}
|
||||||
|
|
||||||
private _buildQueries(): void {
|
private _buildQueries(): void {
|
||||||
if (isPresent(this._proto)) {
|
if (isPresent(this._proto)) {
|
||||||
this._strategy.buildQueries();
|
this._strategy.buildQueries();
|
||||||
|
@ -1158,6 +1156,7 @@ interface _ElementInjectorStrategy {
|
||||||
getComponent(): any;
|
getComponent(): any;
|
||||||
isComponentKey(key: Key): boolean;
|
isComponentKey(key: Key): boolean;
|
||||||
buildQueries(): void;
|
buildQueries(): void;
|
||||||
|
addDirectivesMatchingQuery(q: Query, res: any[]): void;
|
||||||
getObjByKeyId(keyId: number, visibility: number): any;
|
getObjByKeyId(keyId: number, visibility: number): any;
|
||||||
getDirectiveAtIndex(index: number): any;
|
getDirectiveAtIndex(index: number): any;
|
||||||
getComponentBinding(): DirectiveBinding;
|
getComponentBinding(): DirectiveBinding;
|
||||||
|
@ -1234,17 +1233,18 @@ class ElementInjectorInlineStrategy implements _ElementInjectorStrategy {
|
||||||
|
|
||||||
hydrate(): void {
|
hydrate(): void {
|
||||||
var p = this._protoStrategy;
|
var p = this._protoStrategy;
|
||||||
|
var e = this._ei;
|
||||||
|
|
||||||
if (isPresent(p._keyId0)) this.getObjByKeyId(p._keyId0, LIGHT_DOM_AND_SHADOW_DOM);
|
if (isPresent(p._keyId0) && isBlank(this._obj0)) this._obj0 = e._new(p._binding0);
|
||||||
if (isPresent(p._keyId1)) this.getObjByKeyId(p._keyId1, LIGHT_DOM_AND_SHADOW_DOM);
|
if (isPresent(p._keyId1) && isBlank(this._obj1)) this._obj1 = e._new(p._binding1);
|
||||||
if (isPresent(p._keyId2)) this.getObjByKeyId(p._keyId2, LIGHT_DOM_AND_SHADOW_DOM);
|
if (isPresent(p._keyId2) && isBlank(this._obj2)) this._obj2 = e._new(p._binding2);
|
||||||
if (isPresent(p._keyId3)) this.getObjByKeyId(p._keyId3, LIGHT_DOM_AND_SHADOW_DOM);
|
if (isPresent(p._keyId3) && isBlank(this._obj3)) this._obj3 = e._new(p._binding3);
|
||||||
if (isPresent(p._keyId4)) this.getObjByKeyId(p._keyId4, LIGHT_DOM_AND_SHADOW_DOM);
|
if (isPresent(p._keyId4) && isBlank(this._obj4)) this._obj4 = e._new(p._binding4);
|
||||||
if (isPresent(p._keyId5)) this.getObjByKeyId(p._keyId5, LIGHT_DOM_AND_SHADOW_DOM);
|
if (isPresent(p._keyId5) && isBlank(this._obj5)) this._obj5 = e._new(p._binding5);
|
||||||
if (isPresent(p._keyId6)) this.getObjByKeyId(p._keyId6, LIGHT_DOM_AND_SHADOW_DOM);
|
if (isPresent(p._keyId6) && isBlank(this._obj6)) this._obj6 = e._new(p._binding6);
|
||||||
if (isPresent(p._keyId7)) this.getObjByKeyId(p._keyId7, LIGHT_DOM_AND_SHADOW_DOM);
|
if (isPresent(p._keyId7) && isBlank(this._obj7)) this._obj7 = e._new(p._binding7);
|
||||||
if (isPresent(p._keyId8)) this.getObjByKeyId(p._keyId8, LIGHT_DOM_AND_SHADOW_DOM);
|
if (isPresent(p._keyId8) && isBlank(this._obj8)) this._obj8 = e._new(p._binding8);
|
||||||
if (isPresent(p._keyId9)) this.getObjByKeyId(p._keyId9, LIGHT_DOM_AND_SHADOW_DOM);
|
if (isPresent(p._keyId9) && isBlank(this._obj9)) this._obj9 = e._new(p._binding9);
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponent(): any { return this._obj0; }
|
getComponent(): any { return this._obj0; }
|
||||||
|
@ -1288,6 +1288,20 @@ class ElementInjectorInlineStrategy implements _ElementInjectorStrategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addDirectivesMatchingQuery(query: Query, list: any[]): void {
|
||||||
|
var p = this._protoStrategy;
|
||||||
|
if (isPresent(p._binding0) && p._binding0.key.token === query.selector) list.push(this._obj0);
|
||||||
|
if (isPresent(p._binding1) && p._binding1.key.token === query.selector) list.push(this._obj1);
|
||||||
|
if (isPresent(p._binding2) && p._binding2.key.token === query.selector) list.push(this._obj2);
|
||||||
|
if (isPresent(p._binding3) && p._binding3.key.token === query.selector) list.push(this._obj3);
|
||||||
|
if (isPresent(p._binding4) && p._binding4.key.token === query.selector) list.push(this._obj4);
|
||||||
|
if (isPresent(p._binding5) && p._binding5.key.token === query.selector) list.push(this._obj5);
|
||||||
|
if (isPresent(p._binding6) && p._binding6.key.token === query.selector) list.push(this._obj6);
|
||||||
|
if (isPresent(p._binding7) && p._binding7.key.token === query.selector) list.push(this._obj7);
|
||||||
|
if (isPresent(p._binding8) && p._binding8.key.token === query.selector) list.push(this._obj8);
|
||||||
|
if (isPresent(p._binding9) && p._binding9.key.token === query.selector) list.push(this._obj9);
|
||||||
|
}
|
||||||
|
|
||||||
getObjByKeyId(keyId: number, visibility: number): any {
|
getObjByKeyId(keyId: number, visibility: number): any {
|
||||||
var p = this._protoStrategy;
|
var p = this._protoStrategy;
|
||||||
|
|
||||||
|
@ -1406,8 +1420,8 @@ class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy {
|
||||||
var p = this._protoStrategy;
|
var p = this._protoStrategy;
|
||||||
|
|
||||||
for (var i = 0; i < p._keyIds.length; i++) {
|
for (var i = 0; i < p._keyIds.length; i++) {
|
||||||
if (isPresent(p._keyIds[i])) {
|
if (isPresent(p._keyIds[i]) && isBlank(this._objs[i])) {
|
||||||
this.getObjByKeyId(p._keyIds[i], LIGHT_DOM_AND_SHADOW_DOM);
|
this._objs[i] = this._ei._new(p._bindings[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1429,6 +1443,15 @@ class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addDirectivesMatchingQuery(query: Query, list: any[]): void {
|
||||||
|
var p = this._protoStrategy;
|
||||||
|
|
||||||
|
for (var i = 0; i < p._bindings.length; i++) {
|
||||||
|
if (p._bindings[i].key.token === query.selector) list.push(this._objs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
getObjByKeyId(keyId: number, visibility: number): any {
|
getObjByKeyId(keyId: number, visibility: number): any {
|
||||||
var p = this._protoStrategy;
|
var p = this._protoStrategy;
|
||||||
|
|
||||||
|
@ -1518,8 +1541,6 @@ class QueryRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _aggregateDirective(inj: ElementInjector, aggregator: List<any>): void {
|
private _aggregateDirective(inj: ElementInjector, aggregator: List<any>): void {
|
||||||
if (inj.hasDirective(this.query.selector)) {
|
inj.addDirectivesMatchingQuery(this.query, aggregator);
|
||||||
aggregator.push(inj.get(this.query.selector));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ class ListWrapper {
|
||||||
|
|
||||||
bool isListLikeIterable(obj) => obj is Iterable;
|
bool isListLikeIterable(obj) => obj is Iterable;
|
||||||
|
|
||||||
List<T> iterableToList(Iterable<T> ii) => ii.toList();
|
List iterableToList(Iterable ii) => ii.toList();
|
||||||
|
|
||||||
void iterateListLike(iter, fn(item)) {
|
void iterateListLike(iter, fn(item)) {
|
||||||
assert(iter is Iterable);
|
assert(iter is Iterable);
|
||||||
|
|
|
@ -253,7 +253,7 @@ export function iterateListLike(obj, fn: Function) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function iterableToList<T>(ii:Iterable<T>):List<T> {
|
export function iterableToList<T>(ii: any): List<T> {
|
||||||
var res = [];
|
var res = [];
|
||||||
for (var i of ii) {
|
for (var i of ii) {
|
||||||
res.push(i);
|
res.push(i);
|
||||||
|
|
|
@ -38,7 +38,8 @@ export function setUpControl(c: Control, dir: NgControl) {
|
||||||
|
|
||||||
export function composeNgValidator(ngValidators: QueryList<NgValidator>): Function {
|
export function composeNgValidator(ngValidators: QueryList<NgValidator>): Function {
|
||||||
if (isBlank(ngValidators)) return Validators.nullValidator;
|
if (isBlank(ngValidators)) return Validators.nullValidator;
|
||||||
return Validators.compose(iterableToList(ngValidators).map(v => v.validator));
|
return Validators.compose(
|
||||||
|
(<List<NgValidator>>iterableToList(ngValidators)).map(v => v.validator));
|
||||||
}
|
}
|
||||||
|
|
||||||
function _throwError(dir: NgControl, message: string): void {
|
function _throwError(dir: NgControl, message: string): void {
|
||||||
|
@ -49,5 +50,5 @@ function _throwError(dir: NgControl, message: string): void {
|
||||||
export function setProperty(renderer: Renderer, elementRef: ElementRef, propName: string,
|
export function setProperty(renderer: Renderer, elementRef: ElementRef, propName: string,
|
||||||
propValue: any) {
|
propValue: any) {
|
||||||
renderer.setElementProperty(elementRef.parentView.render, elementRef.boundElementIndex, propName,
|
renderer.setElementProperty(elementRef.parentView.render, elementRef.boundElementIndex, propName,
|
||||||
propValue);
|
propValue);
|
||||||
}
|
}
|
|
@ -486,20 +486,6 @@ export function main() {
|
||||||
expect(pei.getBindingAtIndex(i).key.token).toBe(i);
|
expect(pei.getBindingAtIndex(i).key.token).toBe(i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw whenever multiple directives declare the same host injectable', () => {
|
|
||||||
expect(() => {
|
|
||||||
createPei(null, 0, [
|
|
||||||
DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component({
|
|
||||||
hostInjector: [bind('injectable1').toValue('injectable1')]
|
|
||||||
})),
|
|
||||||
DirectiveBinding.createFromType(SomeOtherDirective, new dirAnn.Component({
|
|
||||||
hostInjector: [bind('injectable1').toValue('injectable2')]
|
|
||||||
}))
|
|
||||||
]);
|
|
||||||
}).toThrowError('Multiple directives defined the same host injectable: "injectable1"');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -968,12 +954,47 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain directives on the same injector', () => {
|
it('should contain directives on the same injector', () => {
|
||||||
var inj = injector(ListWrapper.concat([NeedsQuery, CountingDirective], extraBindings), null,
|
var inj = injector(ListWrapper.concat([
|
||||||
|
NeedsQuery,
|
||||||
|
CountingDirective
|
||||||
|
], extraBindings), null,
|
||||||
false, preBuildObjects);
|
false, preBuildObjects);
|
||||||
|
|
||||||
expectDirectives(inj.get(NeedsQuery).query, CountingDirective, [0]);
|
expectDirectives(inj.get(NeedsQuery).query, CountingDirective, [0]);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should contain multiple directives from the same injector', () => {
|
||||||
|
var inj = injector(ListWrapper.concat([
|
||||||
|
NeedsQuery,
|
||||||
|
CountingDirective,
|
||||||
|
FancyCountingDirective,
|
||||||
|
bind(CountingDirective).toAlias(FancyCountingDirective)
|
||||||
|
], extraBindings), null,
|
||||||
|
false, preBuildObjects);
|
||||||
|
|
||||||
|
expect(inj.get(NeedsQuery).query.length).toEqual(2);
|
||||||
|
expect(inj.get(NeedsQuery).query.first).toBeAnInstanceOf(CountingDirective);
|
||||||
|
expect(inj.get(NeedsQuery).query.last).toBeAnInstanceOf(FancyCountingDirective);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should contain multiple directives from the same injector after linking', () => {
|
||||||
|
var inj = parentChildInjectors([], ListWrapper.concat([
|
||||||
|
NeedsQuery,
|
||||||
|
CountingDirective,
|
||||||
|
FancyCountingDirective,
|
||||||
|
bind(CountingDirective).toAlias(FancyCountingDirective)
|
||||||
|
], extraBindings));
|
||||||
|
|
||||||
|
var parent = inj.parent;
|
||||||
|
|
||||||
|
inj.unlink();
|
||||||
|
inj.link(parent);
|
||||||
|
|
||||||
|
expect(inj.get(NeedsQuery).query.length).toEqual(2);
|
||||||
|
expect(inj.get(NeedsQuery).query.first).toBeAnInstanceOf(CountingDirective);
|
||||||
|
expect(inj.get(NeedsQuery).query.last).toBeAnInstanceOf(FancyCountingDirective);
|
||||||
|
})
|
||||||
|
|
||||||
it('should contain the element when no directives are bound to the var binding', () => {
|
it('should contain the element when no directives are bound to the var binding', () => {
|
||||||
var dirs = [NeedsQueryByVarBindings];
|
var dirs = [NeedsQueryByVarBindings];
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,14 @@ import {
|
||||||
Component,
|
Component,
|
||||||
Directive,
|
Directive,
|
||||||
View,
|
View,
|
||||||
Ancestor
|
Ancestor,
|
||||||
|
NgValidator,
|
||||||
|
forwardRef,
|
||||||
|
Binding
|
||||||
} from 'angular2/angular2';
|
} from 'angular2/angular2';
|
||||||
import {formDirectives, NgControl, Validators, NgForm} from 'angular2/forms';
|
import {formDirectives, NgControl, Validators, NgForm} from 'angular2/forms';
|
||||||
|
|
||||||
import {RegExpWrapper, print, isPresent} from 'angular2/src/facade/lang';
|
import {RegExpWrapper, print, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
|
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
|
||||||
|
@ -33,7 +36,10 @@ class CheckoutModel {
|
||||||
/**
|
/**
|
||||||
* Custom validator.
|
* Custom validator.
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[credit-card]'})
|
const creditCardValidatorBinding =
|
||||||
|
CONST_EXPR(new Binding(NgValidator, {toAlias: forwardRef(() => CreditCardValidator)}));
|
||||||
|
|
||||||
|
@Directive({selector: '[credit-card]', hostInjector: [creditCardValidatorBinding]})
|
||||||
class CreditCardValidator {
|
class CreditCardValidator {
|
||||||
get validator() { return CreditCardValidator.validate; }
|
get validator() { return CreditCardValidator.validate; }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue