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