refactor(di): removed @Parent
BREAKING CHANGE
    The @Parent annotation has been removed. Use @Ancestor instead.
    @Parent was used to enforce a particular DOM structure (e.g., a pane component is a direct child of the tabs component).
    DI is not the right mechanism to do it. We should enforce it using schema instead.
			
			
This commit is contained in:
		
							parent
							
								
									a472eacc07
								
							
						
					
					
						commit
						6f4a39c337
					
				| @ -10,7 +10,6 @@ export { | ||||
|   InjectableMetadata, | ||||
|   VisibilityMetadata, | ||||
|   SelfMetadata, | ||||
|   ParentMetadata, | ||||
|   AncestorMetadata, | ||||
|   UnboundedMetadata, | ||||
|   DependencyMetadata, | ||||
| @ -24,12 +23,12 @@ export {forwardRef, resolveForwardRef, ForwardRefFn} from './src/di/forward_ref' | ||||
| export { | ||||
|   Injector, | ||||
|   ProtoInjector, | ||||
|   BindingWithVisibility, | ||||
|   DependencyProvider, | ||||
|   PUBLIC_AND_PRIVATE, | ||||
|   PUBLIC, | ||||
|   PRIVATE, | ||||
|   undefinedValue | ||||
| 
 | ||||
| } from './src/di/injector'; | ||||
| export {Binding, BindingBuilder, ResolvedBinding, Dependency, bind} from './src/di/binding'; | ||||
| export {Key, KeyRegistry, TypeLiteral} from './src/di/key'; | ||||
|  | ||||
| @ -262,7 +262,6 @@ There are five kinds of visibilities: | ||||
| 
 | ||||
| * (no annotation): Inject dependent directives only if they are on the current element. | ||||
| * `@ancestor`: Inject a directive if it is at any element above the current element. | ||||
| * `@parent`: Inject a directive which is a direct parent of the current element. | ||||
| * `@child`: Inject a list of direct children which match a given type. (Used with `Query`) | ||||
| * `@descendant`: Inject a list of any children which match a given type. (Used with `Query`) | ||||
| 
 | ||||
| @ -301,7 +300,7 @@ class FieldSet {                     | | ||||
| class Field {                        | | ||||
|   constructor(                       | | ||||
|     @ancestor field:Form,            | | ||||
|     @parent field:FieldSet,          | | ||||
|     @ancestor field:FieldSet,        | | ||||
|   ) { ... }                          | | ||||
| }                                    | | ||||
|                                      | | ||||
| @ -337,7 +336,7 @@ Shadow DOM provides an encapsulation for components, so as a general rule it doe | ||||
| }) | ||||
| class Kid { | ||||
|   constructor( | ||||
|     @Parent() dad:Dad, | ||||
|     @Ancestor() dad:Dad, | ||||
|     @Optional() grandpa:Grandpa | ||||
|   ) { | ||||
|     this.name = 'Billy'; | ||||
| @ -354,7 +353,7 @@ class Kid { | ||||
|   directives: [Kid] | ||||
| }) | ||||
| class Dad { | ||||
|   constructor(@Parent() dad:Grandpa) { | ||||
|   constructor(@Ancestor() dad:Grandpa) { | ||||
|     this.name = 'Joe Jr'; | ||||
|     this.dad = dad.name; | ||||
|   } | ||||
|  | ||||
| @ -58,8 +58,6 @@ import {DEFAULT} from 'angular2/change_detection'; | ||||
|  *    Shadow DOM root. Current element is not included in the resolution, therefore even if it could | ||||
|  * resolve it, it will | ||||
|  *    be ignored. | ||||
|  * - `@Parent() directive:DirectiveType`: any directive that matches the type on a direct parent | ||||
|  * element only. | ||||
|  * - `@Query(DirectiveType) query:QueryList<DirectiveType>`: A live collection of direct child | ||||
|  * directives. | ||||
|  * - `@QueryDescendants(DirectiveType) query:QueryList<DirectiveType>`: A live collection of any | ||||
| @ -163,27 +161,6 @@ import {DEFAULT} from 'angular2/change_detection'; | ||||
|  * This directive would be instantiated with `Dependency` declared at the same element, in this case | ||||
|  * `dependency="3"`. | ||||
|  * | ||||
|  * | ||||
|  * ### Injecting a directive from a direct parent element | ||||
|  * | ||||
|  * Directives can inject other directives declared on a direct parent element. By definition, a | ||||
|  * directive with a | ||||
|  * `@Parent` annotation does not attempt to resolve dependencies for the current element, even if | ||||
|  * this would satisfy | ||||
|  * the dependency. | ||||
|  * | ||||
|  * ``` | ||||
|  * @Directive({ selector: '[my-directive]' }) | ||||
|  * class MyDirective { | ||||
|  *   constructor(@Parent() dependency: Dependency) { | ||||
|  *     expect(dependency.id).toEqual(2); | ||||
|  *   } | ||||
|  * } | ||||
|  * ``` | ||||
|  * This directive would be instantiated with `Dependency` declared at the parent element, in this | ||||
|  * case `dependency="2"`. | ||||
|  * | ||||
|  * | ||||
|  * ### Injecting a directive from any ancestor elements | ||||
|  * | ||||
|  * Directives can inject other directives declared on any ancestor element (in the current Shadow | ||||
| @ -201,8 +178,8 @@ import {DEFAULT} from 'angular2/change_detection'; | ||||
|  * } | ||||
|  * ``` | ||||
|  * | ||||
|  * Unlike the `@Parent` which only checks the parent, `@Ancestor` checks the parent, as well as its | ||||
|  * parents recursively. If `dependency="2"` didn't exist on the direct parent, this injection would | ||||
|  * `@Ancestor` checks the parent, as well as its parents recursively. If `dependency="2"` didn't | ||||
|  * exist on the direct parent, this injection would | ||||
|  * have returned | ||||
|  * `dependency="1"`. | ||||
|  * | ||||
|  | ||||
| @ -397,7 +397,7 @@ export class ProtoElementInjector { | ||||
|               public directiveVariableBindings: Map<string, number>) { | ||||
|     var length = bwv.length; | ||||
| 
 | ||||
|     this.protoInjector = new ProtoInjector(bwv, distanceToParent); | ||||
|     this.protoInjector = new ProtoInjector(bwv); | ||||
| 
 | ||||
|     this.eventEmitterAccessors = ListWrapper.createFixedSize(length); | ||||
|     this.hostActionAccessors = ListWrapper.createFixedSize(length); | ||||
|  | ||||
| @ -31,13 +31,6 @@ class Self extends SelfMetadata { | ||||
| 	const Self(): super(); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * {@link ParentMetadata}. | ||||
|  */ | ||||
| class Parent extends ParentMetadata { | ||||
| 	const Parent({bool self}): super(self:self); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * {@link AncestorMetadata}. | ||||
|  */ | ||||
|  | ||||
| @ -4,7 +4,6 @@ import { | ||||
|   InjectableMetadata, | ||||
|   SelfMetadata, | ||||
|   VisibilityMetadata, | ||||
|   ParentMetadata, | ||||
|   AncestorMetadata, | ||||
|   UnboundedMetadata | ||||
| } from './metadata'; | ||||
| @ -42,14 +41,6 @@ export interface SelfFactory { | ||||
|   new (): SelfMetadata; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Factory for creating {@link ParentMetadata}. | ||||
|  */ | ||||
| export interface ParentFactory { | ||||
|   (visibility?: {self: boolean}): any; | ||||
|   new (visibility?: {self: boolean}): ParentMetadata; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Factory for creating {@link AncestorMetadata}. | ||||
|  */ | ||||
| @ -86,11 +77,6 @@ export var Injectable: InjectableFactory = <InjectableFactory>makeDecorator(Inje | ||||
|  */ | ||||
| export var Self: SelfFactory = makeParamDecorator(SelfMetadata); | ||||
| 
 | ||||
| /** | ||||
|  * Factory for creating {@link ParentMetadata}. | ||||
|  */ | ||||
| export var Parent: ParentFactory = makeParamDecorator(ParentMetadata); | ||||
| 
 | ||||
| /** | ||||
|  * Factory for creating {@link AncestorMetadata}. | ||||
|  */ | ||||
|  | ||||
| @ -14,7 +14,7 @@ import { | ||||
| import {FunctionWrapper, Type, isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang'; | ||||
| import {Key} from './key'; | ||||
| import {resolveForwardRef} from './forward_ref'; | ||||
| import {VisibilityMetadata, DEFAULT_VISIBILITY} from './metadata'; | ||||
| import {VisibilityMetadata, DEFAULT_VISIBILITY, SelfMetadata, AncestorMetadata} from './metadata'; | ||||
| 
 | ||||
| const _constructing = CONST_EXPR(new Object()); | ||||
| const _notFound = CONST_EXPR(new Object()); | ||||
| @ -175,7 +175,7 @@ export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy { | ||||
| export class ProtoInjector { | ||||
|   _strategy: ProtoInjectorStrategy; | ||||
| 
 | ||||
|   constructor(bwv: BindingWithVisibility[], public distanceToParent: number) { | ||||
|   constructor(bwv: BindingWithVisibility[]) { | ||||
|     this._strategy = bwv.length > _MAX_CONSTRUCTION_COUNTER ? | ||||
|                          new ProtoInjectorDynamicStrategy(this, bwv) : | ||||
|                          new ProtoInjectorInlineStrategy(this, bwv); | ||||
| @ -459,7 +459,7 @@ export class Injector { | ||||
|   static fromResolvedBindings(bindings: List<ResolvedBinding>, | ||||
|                               depProvider: DependencyProvider = null): Injector { | ||||
|     var bd = bindings.map(b => new BindingWithVisibility(b, PUBLIC)); | ||||
|     var proto = new ProtoInjector(bd, 0); | ||||
|     var proto = new ProtoInjector(bd); | ||||
|     var inj = new Injector(proto, null, depProvider); | ||||
|     return inj; | ||||
|   } | ||||
| @ -542,7 +542,7 @@ export class Injector { | ||||
|   createChildFromResolved(bindings: List<ResolvedBinding>, | ||||
|                           depProvider: DependencyProvider = null): Injector { | ||||
|     var bd = bindings.map(b => new BindingWithVisibility(b, PUBLIC)); | ||||
|     var proto = new ProtoInjector(bd, 1); | ||||
|     var proto = new ProtoInjector(bd); | ||||
|     var inj = new Injector(proto, null, depProvider); | ||||
|     inj._parent = this; | ||||
|     return inj; | ||||
| @ -678,49 +678,79 @@ export class Injector { | ||||
|       return this; | ||||
|     } | ||||
| 
 | ||||
|     var inj = this; | ||||
|     var lastInjector = false; | ||||
|     var depth = depVisibility.depth; | ||||
|     if (depVisibility instanceof SelfMetadata) { | ||||
|       return this._getByKeySelf(key, optional, bindingVisibility); | ||||
| 
 | ||||
|     if (!depVisibility.includeSelf) { | ||||
|       depth -= inj._proto.distanceToParent; | ||||
|     } else if (depVisibility instanceof AncestorMetadata) { | ||||
|       return this._getByKeyAncestor(key, optional, bindingVisibility, depVisibility.includeSelf); | ||||
| 
 | ||||
|       if (inj._isBoundary) { | ||||
|         if (depVisibility.crossBoundaries) { | ||||
|           bindingVisibility = PUBLIC_AND_PRIVATE; | ||||
|         } else { | ||||
|           bindingVisibility = PRIVATE; | ||||
|           lastInjector = true; | ||||
|         } | ||||
|       } | ||||
|       inj = inj._parent; | ||||
|     } | ||||
| 
 | ||||
|     while (inj != null && depth >= 0) { | ||||
|       var obj = inj._strategy.getObjByKeyId(key.id, bindingVisibility); | ||||
|       if (obj !== undefinedValue) return obj; | ||||
| 
 | ||||
|       depth -= inj._proto.distanceToParent; | ||||
| 
 | ||||
|       if (lastInjector) break; | ||||
| 
 | ||||
|       if (inj._isBoundary) { | ||||
|         if (depVisibility.crossBoundaries) { | ||||
|           bindingVisibility = PUBLIC_AND_PRIVATE; | ||||
|         } else { | ||||
|           bindingVisibility = PRIVATE; | ||||
|           lastInjector = true; | ||||
|         } | ||||
|       } | ||||
|       inj = inj._parent; | ||||
|     } else { | ||||
|       return this._getByKeyUnbounded(key, optional, bindingVisibility, depVisibility.includeSelf); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   _throwOrNull(key: Key, optional: boolean): any { | ||||
|     if (optional) { | ||||
|       return null; | ||||
|     } else { | ||||
|       throw new NoBindingError(key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   _getByKeySelf(key: Key, optional: boolean, bindingVisibility: number): any { | ||||
|     var obj = this._strategy.getObjByKeyId(key.id, bindingVisibility); | ||||
|     return (obj !== undefinedValue) ? obj : this._throwOrNull(key, optional); | ||||
|   } | ||||
| 
 | ||||
|   _getByKeyAncestor(key: Key, optional: boolean, bindingVisibility: number, | ||||
|                     includeSelf: boolean): any { | ||||
|     var inj = this; | ||||
| 
 | ||||
|     if (!includeSelf) { | ||||
|       if (inj._isBoundary) { | ||||
|         return this._getPrivateDependency(key, optional, inj); | ||||
|       } else { | ||||
|         inj = inj._parent; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     while (inj != null) { | ||||
|       var obj = inj._strategy.getObjByKeyId(key.id, bindingVisibility); | ||||
|       if (obj !== undefinedValue) return obj; | ||||
| 
 | ||||
|       if (isPresent(inj._parent) && inj._isBoundary) { | ||||
|         return this._getPrivateDependency(key, optional, inj); | ||||
|       } else { | ||||
|         inj = inj._parent; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return this._throwOrNull(key, optional); | ||||
|   } | ||||
| 
 | ||||
|   _getPrivateDependency(key: Key, optional: boolean, inj: Injector): any { | ||||
|     var obj = inj._parent._strategy.getObjByKeyId(key.id, PRIVATE); | ||||
|     return (obj !== undefinedValue) ? obj : this._throwOrNull(key, optional); | ||||
|   } | ||||
| 
 | ||||
|   _getByKeyUnbounded(key: Key, optional: boolean, bindingVisibility: number, | ||||
|                      includeSelf: boolean): any { | ||||
|     var inj = this; | ||||
|     if (!includeSelf) { | ||||
|       bindingVisibility = inj._isBoundary ? PUBLIC_AND_PRIVATE : PUBLIC; | ||||
|       inj = inj._parent; | ||||
|     } | ||||
| 
 | ||||
|     while (inj != null) { | ||||
|       var obj = inj._strategy.getObjByKeyId(key.id, bindingVisibility); | ||||
|       if (obj !== undefinedValue) return obj; | ||||
| 
 | ||||
|       bindingVisibility = inj._isBoundary ? PUBLIC_AND_PRIVATE : PUBLIC; | ||||
|       inj = inj._parent; | ||||
|     } | ||||
| 
 | ||||
|     return this._throwOrNull(key, optional); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| var INJECTOR_KEY = Key.get(Injector); | ||||
|  | ||||
| @ -42,21 +42,21 @@ export class OptionalMetadata { | ||||
|  * For example: | ||||
|  * | ||||
|  * ``` | ||||
|  * class Parent extends DependencyMetadata {} | ||||
|  * class Exclude extends DependencyMetadata {} | ||||
|  * class NotDependencyProperty {} | ||||
|  * | ||||
|  * class AComponent { | ||||
|  *   constructor(@Parent @NotDependencyProperty aService:AService) {} | ||||
|  *   constructor(@Exclude @NotDependencyProperty aService:AService) {} | ||||
|  * } | ||||
|  * ``` | ||||
|  * | ||||
|  * will create the following dependency: | ||||
|  * | ||||
|  * ``` | ||||
|  * new Dependency(Key.get(AService), [new Parent()]) | ||||
|  * new Dependency(Key.get(AService), [new Exclude()]) | ||||
|  * ``` | ||||
|  * | ||||
|  * The framework can use `new Parent()` to handle the `aService` dependency | ||||
|  * The framework can use `new Exclude()` to handle the `aService` dependency | ||||
|  * in a specific way. | ||||
|  */ | ||||
| @CONST() | ||||
| @ -85,17 +85,16 @@ export class InjectableMetadata { | ||||
| /** | ||||
|  * Specifies how injector should resolve a dependency. | ||||
|  * | ||||
|  * See {@link Self}, {@link Parent}, {@link Ancestor}, {@link Unbounded}. | ||||
|  * See {@link Self}, {@link Ancestor}, {@link Unbounded}. | ||||
|  */ | ||||
| @CONST() | ||||
| export class VisibilityMetadata { | ||||
|   constructor(public depth: number, public crossBoundaries: boolean, public _includeSelf: boolean) { | ||||
|   } | ||||
|   constructor(public crossBoundaries: boolean, public _includeSelf: boolean) {} | ||||
| 
 | ||||
|   get includeSelf(): boolean { return isBlank(this._includeSelf) ? false : this._includeSelf; } | ||||
| 
 | ||||
|   toString(): string { | ||||
|     return `@Visibility(depth: ${this.depth}, crossBoundaries: ${this.crossBoundaries}, includeSelf: ${this.includeSelf}})`; | ||||
|     return `@Visibility(crossBoundaries: ${this.crossBoundaries}, includeSelf: ${this.includeSelf}})`; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -119,46 +118,10 @@ export class VisibilityMetadata { | ||||
|  */ | ||||
| @CONST() | ||||
| export class SelfMetadata extends VisibilityMetadata { | ||||
|   constructor() { super(0, false, true); } | ||||
|   constructor() { super(false, true); } | ||||
|   toString(): string { return `@Self()`; } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Specifies that an injector should retrieve a dependency from the direct parent. | ||||
|  * | ||||
|  * ## Example | ||||
|  * | ||||
|  * ``` | ||||
|  * class Dependency { | ||||
|  * } | ||||
|  * | ||||
|  * class NeedsDependency { | ||||
|  *   constructor(public @Parent() dependency:Dependency) {} | ||||
|  * } | ||||
|  * | ||||
|  * var parent = Injector.resolveAndCreate([ | ||||
|  *   bind(Dependency).toClass(ParentDependency) | ||||
|  * ]); | ||||
|  * var child = parent.resolveAndCreateChild([NeedsDependency, Depedency]); | ||||
|  * var nd = child.get(NeedsDependency); | ||||
|  * expect(nd.dependency).toBeAnInstanceOf(ParentDependency); | ||||
|  * ``` | ||||
|  * | ||||
|  * You can make an injector to retrive a dependency either from itself or its direct parent by | ||||
|  * setting self to true. | ||||
|  * | ||||
|  * ``` | ||||
|  * class NeedsDependency { | ||||
|  *   constructor(public @Parent({self:true}) dependency:Dependency) {} | ||||
|  * } | ||||
|  * ``` | ||||
|  */ | ||||
| @CONST() | ||||
| export class ParentMetadata extends VisibilityMetadata { | ||||
|   constructor({self}: {self?: boolean} = {}) { super(1, false, self); } | ||||
|   toString(): string { return `@Parent(self: ${this.includeSelf}})`; } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Specifies that an injector should retrieve a dependency from any ancestor from the same boundary. | ||||
|  * | ||||
| @ -192,7 +155,7 @@ export class ParentMetadata extends VisibilityMetadata { | ||||
|  */ | ||||
| @CONST() | ||||
| export class AncestorMetadata extends VisibilityMetadata { | ||||
|   constructor({self}: {self?: boolean} = {}) { super(999999, false, self); } | ||||
|   constructor({self}: {self?: boolean} = {}) { super(false, self); } | ||||
|   toString(): string { return `@Ancestor(self: ${this.includeSelf}})`; } | ||||
| } | ||||
| 
 | ||||
| @ -229,7 +192,7 @@ export class AncestorMetadata extends VisibilityMetadata { | ||||
|  */ | ||||
| @CONST() | ||||
| export class UnboundedMetadata extends VisibilityMetadata { | ||||
|   constructor({self}: {self?: boolean} = {}) { super(999999, true, self); } | ||||
|   constructor({self}: {self?: boolean} = {}) { super(true, self); } | ||||
|   toString(): string { return `@Unbounded(self: ${this.includeSelf}})`; } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import {Directive} from 'angular2/annotations'; | ||||
| import {Parent} from 'angular2/di'; | ||||
| import {Ancestor} from 'angular2/di'; | ||||
| import {ViewContainerRef, TemplateRef} from 'angular2/core'; | ||||
| import {isPresent, isBlank, normalizeBlank} from 'angular2/src/facade/lang'; | ||||
| import {ListWrapper, List, MapWrapper, Map} from 'angular2/src/facade/collection'; | ||||
| @ -157,7 +157,7 @@ export class NgSwitchWhen { | ||||
|   _view: SwitchView; | ||||
| 
 | ||||
|   constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef, | ||||
|               @Parent() sswitch: NgSwitch) { | ||||
|               @Ancestor() sswitch: NgSwitch) { | ||||
|     // `_whenDefault` is used as a marker for a not yet initialized value
 | ||||
|     this._value = _whenDefault; | ||||
|     this._switch = sswitch; | ||||
| @ -187,7 +187,7 @@ export class NgSwitchWhen { | ||||
| @Directive({selector: '[ng-switch-default]'}) | ||||
| export class NgSwitchDefault { | ||||
|   constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef, | ||||
|               @Parent() sswitch: NgSwitch) { | ||||
|               @Ancestor() sswitch: NgSwitch) { | ||||
|     sswitch._registerView(_whenDefault, new SwitchView(viewContainer, templateRef)); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -40,7 +40,7 @@ import { | ||||
|   Directive, | ||||
|   LifecycleEvent | ||||
| } from 'angular2/annotations'; | ||||
| import {bind, Injector, Binding, Optional, Inject, Injectable, Self, Parent, Ancestor, Unbounded, InjectMetadata, ParentMetadata} from 'angular2/di'; | ||||
| import {bind, Injector, Binding, Optional, Inject, Injectable, Self, Ancestor, Unbounded, InjectMetadata, AncestorMetadata} from 'angular2/di'; | ||||
| import {AppProtoView, AppView} from 'angular2/src/core/compiler/view'; | ||||
| import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref'; | ||||
| import {TemplateRef} from 'angular2/src/core/compiler/template_ref'; | ||||
| @ -102,18 +102,6 @@ class OptionallyNeedsDirective { | ||||
|   constructor(@Self() @Optional() dependency: SimpleDirective) { this.dependency = dependency; } | ||||
| } | ||||
| 
 | ||||
| @Injectable() | ||||
| class NeedsDirectiveFromParent { | ||||
|   dependency: SimpleDirective; | ||||
|   constructor(@Parent() dependency: SimpleDirective) { this.dependency = dependency; } | ||||
| } | ||||
| 
 | ||||
| @Injectable() | ||||
| class NeedsDirectiveFromParentOrSelf { | ||||
|   dependency: SimpleDirective; | ||||
|   constructor(@Parent({self: true}) dependency: SimpleDirective) { this.dependency = dependency; } | ||||
| } | ||||
| 
 | ||||
| @Injectable() | ||||
| class NeedsDirectiveFromAncestor { | ||||
|   dependency: SimpleDirective; | ||||
| @ -609,7 +597,7 @@ export function main() { | ||||
|                        bind('injectable2') | ||||
|                            .toFactory( | ||||
|                                (val) => `${val}-injectable2`, | ||||
|                                [[new InjectMetadata('injectable1'), new ParentMetadata()]]) | ||||
|                                [[new InjectMetadata('injectable1'), new AncestorMetadata()]]) | ||||
|                      ] | ||||
|                    }))]); | ||||
|                expect(childInj.get('injectable2')).toEqual('injectable1-injectable2'); | ||||
| @ -775,31 +763,6 @@ export function main() { | ||||
|             expect(inj.get(NeedsTemplateRef).templateRef).toEqual(templateRef); | ||||
|           }); | ||||
| 
 | ||||
|           it("should get directives from parent", () => { | ||||
|             var child = parentChildInjectors(ListWrapper.concat([SimpleDirective], extraBindings), | ||||
|                                              [NeedsDirectiveFromParent]); | ||||
| 
 | ||||
|             var d = child.get(NeedsDirectiveFromParent); | ||||
| 
 | ||||
|             expect(d).toBeAnInstanceOf(NeedsDirectiveFromParent); | ||||
|             expect(d.dependency).toBeAnInstanceOf(SimpleDirective); | ||||
|           }); | ||||
| 
 | ||||
|           it("should not return parent's directives on self by default", () => { | ||||
|             expect(() => { | ||||
|               injector(ListWrapper.concat([SimpleDirective, NeedsDirectiveFromParent], extraBindings)); | ||||
|             }).toThrowError(containsRegexp(`No provider for ${stringify(SimpleDirective) }`)); | ||||
|           }); | ||||
| 
 | ||||
|           it("should return parent's directives on self when explicitly specified", () => { | ||||
|             var inj = injector(ListWrapper.concat([SimpleDirective, NeedsDirectiveFromParentOrSelf], extraBindings)); | ||||
| 
 | ||||
|             var d = inj.get(NeedsDirectiveFromParentOrSelf); | ||||
| 
 | ||||
|             expect(d).toBeAnInstanceOf(NeedsDirectiveFromParentOrSelf); | ||||
|             expect(d.dependency).toBeAnInstanceOf(SimpleDirective); | ||||
|           }); | ||||
| 
 | ||||
|           it("should get directives from ancestor", () => { | ||||
|             var child = parentChildInjectors(ListWrapper.concat([SimpleDirective], extraBindings), | ||||
|                                              [NeedsDirectiveFromAncestor]); | ||||
| @ -822,9 +785,9 @@ export function main() { | ||||
|           }); | ||||
| 
 | ||||
|           it("should throw when a dependency cannot be resolved", () => { | ||||
|             expect(() => injector(ListWrapper.concat([NeedsDirectiveFromParent], extraBindings))) | ||||
|             expect(() => injector(ListWrapper.concat([NeedsDirectiveFromAncestor], extraBindings))) | ||||
|                 .toThrowError(containsRegexp( | ||||
|                     `No provider for ${stringify(SimpleDirective) }! (${stringify(NeedsDirectiveFromParent) } -> ${stringify(SimpleDirective) })`)); | ||||
|                     `No provider for ${stringify(SimpleDirective) }! (${stringify(NeedsDirectiveFromAncestor) } -> ${stringify(SimpleDirective) })`)); | ||||
|           }); | ||||
| 
 | ||||
|           it("should inject null when an optional dependency cannot be resolved", () => { | ||||
| @ -853,30 +816,30 @@ export function main() { | ||||
|                 .toThrowError(`Index ${firsIndexOut} is out-of-bounds.`); | ||||
|           }); | ||||
| 
 | ||||
|             it("should instantiate directives that depend on the containing component", () => { | ||||
|               var directiveBinding = | ||||
|                   DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component()); | ||||
|               var shadow = hostShadowInjectors(ListWrapper.concat([directiveBinding], extraBindings), | ||||
|                                                [NeedsDirective]); | ||||
|           it("should instantiate directives that depend on the containing component", () => { | ||||
|             var directiveBinding = | ||||
|                 DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component()); | ||||
|             var shadow = hostShadowInjectors(ListWrapper.concat([directiveBinding], extraBindings), | ||||
|                                              [NeedsDirectiveFromAncestor]); | ||||
| 
 | ||||
|               var d = shadow.get(NeedsDirective); | ||||
|               expect(d).toBeAnInstanceOf(NeedsDirective); | ||||
|               expect(d.dependency).toBeAnInstanceOf(SimpleDirective); | ||||
|             }); | ||||
|             var d = shadow.get(NeedsDirectiveFromAncestor); | ||||
|             expect(d).toBeAnInstanceOf(NeedsDirectiveFromAncestor); | ||||
|             expect(d.dependency).toBeAnInstanceOf(SimpleDirective); | ||||
|           }); | ||||
| 
 | ||||
|             it("should not instantiate directives that depend on other directives in the containing component's ElementInjector", | ||||
|                () => { | ||||
|                  var directiveBinding = | ||||
|                      DirectiveBinding.createFromType(SomeOtherDirective, new dirAnn.Component()); | ||||
|                  expect(() => | ||||
|                         { | ||||
|                           hostShadowInjectors( | ||||
|                               ListWrapper.concat([directiveBinding, SimpleDirective], extraBindings), | ||||
|                               [NeedsDirective]); | ||||
|                         }) | ||||
|                      .toThrowError(containsRegexp( | ||||
|                          `No provider for ${stringify(SimpleDirective) }! (${stringify(NeedsDirective) } -> ${stringify(SimpleDirective) })`)); | ||||
|                }); | ||||
|           it("should not instantiate directives that depend on other directives in the containing component's ElementInjector", | ||||
|              () => { | ||||
|                var directiveBinding = | ||||
|                    DirectiveBinding.createFromType(SomeOtherDirective, new dirAnn.Component()); | ||||
|                expect(() => | ||||
|                       { | ||||
|                         hostShadowInjectors( | ||||
|                             ListWrapper.concat([directiveBinding, SimpleDirective], extraBindings), | ||||
|                             [NeedsDirective]); | ||||
|                       }) | ||||
|                    .toThrowError(containsRegexp( | ||||
|                        `No provider for ${stringify(SimpleDirective) }! (${stringify(NeedsDirective) } -> ${stringify(SimpleDirective) })`)); | ||||
|              }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("lifecycle", () => { | ||||
|  | ||||
| @ -42,7 +42,6 @@ import { | ||||
|   forwardRef, | ||||
|   OpaqueToken, | ||||
|   Inject, | ||||
|   Parent, | ||||
|   Ancestor, | ||||
|   Unbounded, | ||||
|   UnboundedMetadata | ||||
| @ -423,8 +422,8 @@ export function main() { | ||||
|          inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { | ||||
|            tcb.overrideView(MyComp, new viewAnn.View({ | ||||
|                 template: | ||||
|                     '<some-directive><toolbar><template toolbarpart var-toolbar-prop="toolbarProp">{{ctxProp}},{{toolbarProp}},<cmp-with-parent></cmp-with-parent></template></toolbar></some-directive>', | ||||
|                 directives: [SomeDirective, CompWithParent, ToolbarComponent, ToolbarPart] | ||||
|                     '<some-directive><toolbar><template toolbarpart var-toolbar-prop="toolbarProp">{{ctxProp}},{{toolbarProp}},<cmp-with-ancestor></cmp-with-ancestor></template></toolbar></some-directive>', | ||||
|                 directives: [SomeDirective, CompWithAncestor, ToolbarComponent, ToolbarPart] | ||||
|               })) | ||||
|                .createAsync(MyComp) | ||||
|                .then((rootTC) => { | ||||
| @ -433,7 +432,7 @@ export function main() { | ||||
| 
 | ||||
|                  expect(rootTC.nativeElement) | ||||
|                      .toHaveText( | ||||
|                          'TOOLBAR(From myComp,From toolbar,Component with an injected parent)'); | ||||
|                          'TOOLBAR(From myComp,From toolbar,Component with an injected ancestor)'); | ||||
| 
 | ||||
|                  async.done(); | ||||
|                }); | ||||
| @ -663,25 +662,6 @@ export function main() { | ||||
|                        })})); | ||||
|       }); | ||||
| 
 | ||||
|       it('should create a component that injects a @Parent', | ||||
|          inject( | ||||
|              [TestComponentBuilder, AsyncTestCompleter], | ||||
|              (tcb: TestComponentBuilder, async) => { | ||||
|                  tcb.overrideView(MyComp, new viewAnn.View({ | ||||
|                       template: | ||||
|                           '<some-directive><cmp-with-parent #child></cmp-with-parent></some-directive>', | ||||
|                       directives: [SomeDirective, CompWithParent] | ||||
|                     })) | ||||
| 
 | ||||
|                      .createAsync(MyComp) | ||||
|                      .then((rootTC) => { | ||||
| 
 | ||||
|                        var childComponent = rootTC.componentViewChildren[0].getLocal('child'); | ||||
|                        expect(childComponent.myParent).toBeAnInstanceOf(SomeDirective); | ||||
| 
 | ||||
|                        async.done(); | ||||
|                      })})); | ||||
| 
 | ||||
|       it('should create a component that injects an @Ancestor', | ||||
|          inject([TestComponentBuilder, AsyncTestCompleter], | ||||
|                 (tcb: TestComponentBuilder, async) => { | ||||
| @ -1497,14 +1477,6 @@ class SomeDirective { | ||||
| 
 | ||||
| class SomeDirectiveMissingAnnotation {} | ||||
| 
 | ||||
| @Component({selector: 'cmp-with-parent'}) | ||||
| @View({template: '<p>Component with an injected parent</p>', directives: [SomeDirective]}) | ||||
| @Injectable() | ||||
| class CompWithParent { | ||||
|   myParent: SomeDirective; | ||||
|   constructor(@Parent() someComp: SomeDirective) { this.myParent = someComp; } | ||||
| } | ||||
| 
 | ||||
| @Component({selector: 'cmp-with-ancestor'}) | ||||
| @View({template: '<p>Component with an injected ancestor</p>', directives: [SomeDirective]}) | ||||
| @Injectable() | ||||
| @ -1673,7 +1645,7 @@ class PrivateImpl extends PublicApi { | ||||
| @Directive({selector: '[needs-public-api]'}) | ||||
| @Injectable() | ||||
| class NeedsPublicApi { | ||||
|   constructor(@Parent() api: PublicApi) { expect(api instanceof PrivateImpl).toBe(true); } | ||||
|   constructor(@Ancestor() api: PublicApi) { expect(api instanceof PrivateImpl).toBe(true); } | ||||
| } | ||||
| 
 | ||||
| @Directive({selector: '[toolbarpart]'}) | ||||
|  | ||||
| @ -10,19 +10,27 @@ import { | ||||
| } from 'angular2/test_lib'; | ||||
| import { | ||||
|   Injector, | ||||
|   ProtoInjector, | ||||
|   bind, | ||||
|   ResolvedBinding, | ||||
|   Key, | ||||
|   forwardRef, | ||||
|   DependencyMetadata, | ||||
|   Injectable, | ||||
|   InjectMetadata | ||||
|   InjectMetadata, | ||||
|   SelfMetadata, | ||||
|   AncestorMetadata, | ||||
|   UnboundedMetadata, | ||||
|   Optional, | ||||
|   Inject, | ||||
|   BindingWithVisibility, | ||||
|   PUBLIC, | ||||
|   PRIVATE, | ||||
|   PUBLIC_AND_PRIVATE | ||||
| } from 'angular2/di'; | ||||
| 
 | ||||
| import {InjectorInlineStrategy, InjectorDynamicStrategy} from 'angular2/src/di/injector'; | ||||
| 
 | ||||
| import {Optional, Inject} from 'angular2/src/di/decorators'; | ||||
| 
 | ||||
| class CustomDependencyMetadata extends DependencyMetadata {} | ||||
| 
 | ||||
| class Engine {} | ||||
| @ -362,13 +370,144 @@ export function main() { | ||||
|         expect(engineFromChild).toBeAnInstanceOf(TurboEngine); | ||||
|       }); | ||||
| 
 | ||||
|       it("should give access to direct parent", () => { | ||||
|       it("should give access to parent", () => { | ||||
|         var parent = Injector.resolveAndCreate([]); | ||||
|         var child = parent.resolveAndCreateChild([]); | ||||
|         expect(child.parent).toBe(parent); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
| 
 | ||||
|     describe("depedency resolution", () => { | ||||
|       describe("@Self()", () => { | ||||
|         it("should return a dependency from self", () => { | ||||
|           var inj = Injector.resolveAndCreate( | ||||
|               [Engine, bind(Car).toFactory((e) => new Car(e), [[Engine, new SelfMetadata()]])]); | ||||
| 
 | ||||
|           expect(inj.get(Car)).toBeAnInstanceOf(Car); | ||||
|         }); | ||||
| 
 | ||||
|         it("should throw when not requested binding on self", () => { | ||||
|           var parent = Injector.resolveAndCreate([Engine]); | ||||
|           var child = parent.resolveAndCreateChild( | ||||
|               [bind(Car).toFactory((e) => new Car(e), [[Engine, new SelfMetadata()]])]); | ||||
| 
 | ||||
|           expect(() => child.get(Car)) | ||||
|               .toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`); | ||||
|         }); | ||||
|       }); | ||||
| 
 | ||||
|       describe("@Ancestor()", () => { | ||||
|         it("should return a dependency from same boundary", () => { | ||||
|           var parent = Injector.resolveAndCreate([Engine]); | ||||
|           var child = parent.resolveAndCreateChild( | ||||
|               [bind(Car).toFactory((e) => new Car(e), [[Engine, new AncestorMetadata()]])]); | ||||
| 
 | ||||
|           expect(child.get(Car)).toBeAnInstanceOf(Car); | ||||
|         }); | ||||
| 
 | ||||
|         it("should return a private dependency declared at the boundary", () => { | ||||
|           var engine = Injector.resolve([Engine])[0]; | ||||
|           var protoParent = new ProtoInjector([new BindingWithVisibility(engine, PRIVATE)]); | ||||
|           var parent = new Injector(protoParent); | ||||
| 
 | ||||
|           var child = Injector.resolveAndCreate( | ||||
|               [bind(Car).toFactory((e) => new Car(e), [[Engine, new AncestorMetadata()]])]); | ||||
| 
 | ||||
|           child.internalStrategy.attach(parent, true);  // boundary
 | ||||
| 
 | ||||
|           expect(child.get(Car)).toBeAnInstanceOf(Car); | ||||
|         }); | ||||
| 
 | ||||
|         it("should not return a public dependency declared at the boundary", () => { | ||||
|           var engine = Injector.resolve([Engine])[0]; | ||||
|           var protoParent = new ProtoInjector([new BindingWithVisibility(engine, PUBLIC)]); | ||||
|           var parent = new Injector(protoParent); | ||||
| 
 | ||||
|           var child = Injector.resolveAndCreate( | ||||
|               [bind(Car).toFactory((e) => new Car(e), [[Engine, new AncestorMetadata()]])]); | ||||
| 
 | ||||
|           child.internalStrategy.attach(parent, true);  // boundary
 | ||||
| 
 | ||||
|           expect(() => child.get(Car)) | ||||
|               .toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`); | ||||
|         }); | ||||
| 
 | ||||
|         it("should return a dependency from self when explicitly specified", () => { | ||||
|           var parent = Injector.resolveAndCreate([Engine]); | ||||
|           var child = parent.resolveAndCreateChild([ | ||||
|             bind(Engine) | ||||
|                 .toClass(TurboEngine), | ||||
|             bind(Car) | ||||
|                 .toFactory((e) => new Car(e), [[Engine, new AncestorMetadata({self: true})]]) | ||||
|           ]); | ||||
| 
 | ||||
|           expect(child.get(Car).engine).toBeAnInstanceOf(TurboEngine); | ||||
|         }); | ||||
|       }); | ||||
| 
 | ||||
|       describe("@Unboudned()", () => { | ||||
|         it("should return a private dependency declared at the boundary", () => { | ||||
|           var engine = Injector.resolve([Engine])[0]; | ||||
|           var protoParent = new ProtoInjector([new BindingWithVisibility(engine, PRIVATE)]); | ||||
|           var parent = new Injector(protoParent); | ||||
| 
 | ||||
|           var child = Injector.resolveAndCreate([ | ||||
|             bind(Engine) | ||||
|                 .toClass(BrokenEngine), | ||||
|             bind(Car).toFactory((e) => new Car(e), [[Engine, new UnboundedMetadata()]]) | ||||
|           ]); | ||||
|           child.internalStrategy.attach(parent, true);  // boundary
 | ||||
| 
 | ||||
|           expect(child.get(Car)).toBeAnInstanceOf(Car); | ||||
|         }); | ||||
| 
 | ||||
|         it("should return a public dependency declared at the boundary", () => { | ||||
|           var engine = Injector.resolve([Engine])[0]; | ||||
|           var protoParent = new ProtoInjector([new BindingWithVisibility(engine, PUBLIC)]); | ||||
|           var parent = new Injector(protoParent); | ||||
| 
 | ||||
|           var child = Injector.resolveAndCreate([ | ||||
|             bind(Engine) | ||||
|                 .toClass(BrokenEngine), | ||||
|             bind(Car).toFactory((e) => new Car(e), [[Engine, new UnboundedMetadata()]]) | ||||
|           ]); | ||||
|           child.internalStrategy.attach(parent, true);  // boundary
 | ||||
| 
 | ||||
|           expect(child.get(Car)).toBeAnInstanceOf(Car); | ||||
|         }); | ||||
| 
 | ||||
|         it("should not return a private dependency declared NOT at the boundary", () => { | ||||
|           var engine = Injector.resolve([Engine])[0]; | ||||
|           var protoParent = new ProtoInjector([new BindingWithVisibility(engine, PRIVATE)]); | ||||
|           var parent = new Injector(protoParent); | ||||
| 
 | ||||
|           var child = Injector.resolveAndCreate([ | ||||
|             bind(Engine) | ||||
|                 .toClass(BrokenEngine), | ||||
|             bind(Car).toFactory((e) => new Car(e), [[Engine, new UnboundedMetadata()]]) | ||||
|           ]); | ||||
|           child.internalStrategy.attach(parent, false); | ||||
| 
 | ||||
|           expect(() => child.get(Car)) | ||||
|               .toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`); | ||||
|         }); | ||||
| 
 | ||||
|         it("should return a dependency from self when explicitly specified", () => { | ||||
|           var parent = Injector.resolveAndCreate([Engine]); | ||||
|           var child = parent.resolveAndCreateChild([ | ||||
|             bind(Engine) | ||||
|                 .toClass(TurboEngine), | ||||
|             bind(Car) | ||||
|                 .toFactory((e) => new Car(e), [[Engine, new UnboundedMetadata({self: true})]]) | ||||
|           ]); | ||||
| 
 | ||||
|           expect(child.get(Car).engine).toBeAnInstanceOf(TurboEngine); | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
| 
 | ||||
|     describe('resolve', () => { | ||||
|       it('should resolve and flatten', () => { | ||||
|         var bindings = Injector.resolve([Engine, [BrokenEngine]]); | ||||
|  | ||||
| @ -2,7 +2,7 @@ import { | ||||
|   Component, | ||||
|   Directive, | ||||
|   View, | ||||
|   Parent, | ||||
|   Ancestor, | ||||
|   ElementRef, | ||||
|   DynamicComponentLoader, | ||||
|   ComponentRef, | ||||
| @ -243,7 +243,7 @@ class MdDialogContainer { | ||||
|  */ | ||||
| @Directive({selector: 'md-dialog-content'}) | ||||
| class MdDialogContent { | ||||
|   constructor(@Parent() dialogContainer: MdDialogContainer, elementRef: ElementRef) { | ||||
|   constructor(@Ancestor() dialogContainer: MdDialogContainer, elementRef: ElementRef) { | ||||
|     dialogContainer.contentRef = elementRef; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import {Component, View, Parent, LifecycleEvent} from 'angular2/angular2'; | ||||
| import {Component, View, Ancestor, LifecycleEvent} from 'angular2/angular2'; | ||||
| 
 | ||||
| import {ListWrapper} from 'angular2/src/facade/collection'; | ||||
| import {StringWrapper, isPresent, isString, NumberWrapper} from 'angular2/src/facade/lang'; | ||||
| @ -238,7 +238,7 @@ export class MdGridTile { | ||||
| 
 | ||||
|   isRegisteredWithGridList: boolean; | ||||
| 
 | ||||
|   constructor(@Parent() gridList: MdGridList) { | ||||
|   constructor(@Ancestor() gridList: MdGridList) { | ||||
|     this.gridList = gridList; | ||||
| 
 | ||||
|     // Tiles default to 1x1, but rowspan and colspan can be changed via binding.
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import {Directive, LifecycleEvent, Attribute, Parent} from 'angular2/angular2'; | ||||
| import {Directive, LifecycleEvent, Attribute, Ancestor} from 'angular2/angular2'; | ||||
| 
 | ||||
| import {ObservableWrapper, EventEmitter} from 'angular2/src/facade/async'; | ||||
| 
 | ||||
| @ -73,7 +73,7 @@ export class MdInput { | ||||
|   mdChange: EventEmitter; | ||||
|   mdFocusChange: EventEmitter; | ||||
| 
 | ||||
|   constructor(@Attribute('value') value: string, @Parent() container: MdInputContainer, | ||||
|   constructor(@Attribute('value') value: string, @Ancestor() container: MdInputContainer, | ||||
|               @Attribute('id') id: string) { | ||||
|     // TODO(jelbourn): Remove this when #1402 is done.
 | ||||
|     this.yes = true; | ||||
| @ -111,7 +111,7 @@ export class MdInput { | ||||
| export class MdTextarea extends MdInput { | ||||
|   constructor( | ||||
|       @Attribute('value') value: string, | ||||
|       @Parent() container: MdInputContainer, | ||||
|       @Ancestor() container: MdInputContainer, | ||||
|       @Attribute('id') id: string) { | ||||
|     super(value, container, id); | ||||
|   } | ||||
|  | ||||
| @ -1,12 +1,4 @@ | ||||
| import { | ||||
|   Component, | ||||
|   View, | ||||
|   LifecycleEvent, | ||||
|   Parent, | ||||
|   Ancestor, | ||||
|   Attribute, | ||||
|   Optional | ||||
| } from 'angular2/angular2'; | ||||
| import {Component, View, LifecycleEvent, Ancestor, Attribute, Optional} from 'angular2/angular2'; | ||||
| 
 | ||||
| import {isPresent, StringWrapper, NumberWrapper} from 'angular2/src/facade/lang'; | ||||
| import {ObservableWrapper, EventEmitter} from 'angular2/src/facade/async'; | ||||
| @ -225,7 +217,7 @@ export class MdRadioButton { | ||||
| 
 | ||||
|   role: string; | ||||
| 
 | ||||
|   constructor(@Optional() @Parent() radioGroup: MdRadioGroup, @Attribute('id') id: string, | ||||
|   constructor(@Optional() @Ancestor() radioGroup: MdRadioGroup, @Attribute('id') id: string, | ||||
|               @Attribute('tabindex') tabindex: string, radioDispatcher: MdRadioDispatcher) { | ||||
|     // Assertions. Ideally these should be stripped out by the compiler.
 | ||||
|     // TODO(jelbourn): Assert that there's no name binding AND a parent radio group.
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user