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:
@ -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));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user