feat(di): components can self-publish via publishAs
This commit is contained in:
parent
abc8878547
commit
1a0da11e55
|
@ -815,6 +815,41 @@ export class Component extends Directive {
|
|||
*/
|
||||
injectables:List;
|
||||
|
||||
// TODO(naomib): needs documentation
|
||||
/**
|
||||
* Dependency injection tokens that this component publishes _itself_ to its
|
||||
* children in its view via the application injector.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* Imagine you have parent component that implements the [RpcService]
|
||||
* interface. It can pose as [RpcService] to its children. Child components
|
||||
* do not need to know about this fact. They only need to declare their
|
||||
* dependency on [RpcService] without knowing exactly how it is provided.
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'parent',
|
||||
* publishAs: [RpcService]
|
||||
* })
|
||||
* @View({
|
||||
* template: '<child></child>',
|
||||
* directives: [Child]
|
||||
* })
|
||||
* class Parent implements RpcService {
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'child'
|
||||
* })
|
||||
* class Child {
|
||||
* // Just asks for RpcService; doesn't know that it's Parent.
|
||||
* constructor(RpcService rpc);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
publishAs:List;
|
||||
|
||||
@CONST()
|
||||
constructor({
|
||||
selector,
|
||||
|
@ -827,6 +862,7 @@ export class Component extends Directive {
|
|||
lifecycle,
|
||||
changeDetection = DEFAULT,
|
||||
compileChildren = true,
|
||||
publishAs
|
||||
}:{
|
||||
selector:string,
|
||||
properties:Object,
|
||||
|
@ -837,7 +873,8 @@ export class Component extends Directive {
|
|||
injectables:List,
|
||||
lifecycle:List,
|
||||
changeDetection:string,
|
||||
compileChildren:boolean
|
||||
compileChildren:boolean,
|
||||
publishAs:List
|
||||
}={})
|
||||
{
|
||||
super({
|
||||
|
@ -853,6 +890,7 @@ export class Component extends Directive {
|
|||
|
||||
this.changeDetection = changeDetection;
|
||||
this.injectables = injectables;
|
||||
this.publishAs = publishAs;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -506,6 +506,7 @@ export class ElementInjector extends TreeNode {
|
|||
_query0: QueryRef;
|
||||
_query1: QueryRef;
|
||||
_query2: QueryRef;
|
||||
|
||||
constructor(proto:ProtoElementInjector, parent:ElementInjector) {
|
||||
super(parent);
|
||||
this._proto = proto;
|
||||
|
@ -568,7 +569,14 @@ export class ElementInjector extends TreeNode {
|
|||
this._constructionCounter = 0;
|
||||
}
|
||||
|
||||
instantiateDirectives(lightDomAppInjector:Injector, host:ElementInjector, shadowDomAppInjector:Injector, preBuiltObjects:PreBuiltObjects) {
|
||||
instantiateDirectives(
|
||||
lightDomAppInjector:Injector,
|
||||
host:ElementInjector,
|
||||
preBuiltObjects:PreBuiltObjects) {
|
||||
var shadowDomAppInjector = null;
|
||||
if (this._proto._binding0IsComponent) {
|
||||
shadowDomAppInjector = this._createShadowDomAppInjector(this._proto._binding0, lightDomAppInjector);
|
||||
}
|
||||
this._host = host;
|
||||
this._checkShadowDomAppInjector(shadowDomAppInjector);
|
||||
|
||||
|
@ -578,6 +586,21 @@ export class ElementInjector extends TreeNode {
|
|||
|
||||
var p = this._proto;
|
||||
if (isPresent(p._keyId0)) this._getDirectiveByKeyId(p._keyId0);
|
||||
if (isPresent(shadowDomAppInjector)) {
|
||||
var componentAnnotation:Component = this._proto._binding0.annotation;
|
||||
var publishAs = componentAnnotation.publishAs;
|
||||
if (isPresent(publishAs) && publishAs.length > 0) {
|
||||
// If there's a component directive on this element injector, then
|
||||
// 0-th key must contain the directive itself.
|
||||
// TODO(yjbanov): need to make injector creation faster:
|
||||
// - remove flattening of bindings array
|
||||
// - precalc token key
|
||||
this._shadowDomAppInjector = shadowDomAppInjector.resolveAndCreateChild(
|
||||
ListWrapper.map(publishAs, (token) => {
|
||||
return bind(token).toValue(this.getComponent());
|
||||
}));
|
||||
}
|
||||
}
|
||||
if (isPresent(p._keyId1)) this._getDirectiveByKeyId(p._keyId1);
|
||||
if (isPresent(p._keyId2)) this._getDirectiveByKeyId(p._keyId2);
|
||||
if (isPresent(p._keyId3)) this._getDirectiveByKeyId(p._keyId3);
|
||||
|
@ -589,9 +612,22 @@ export class ElementInjector extends TreeNode {
|
|||
if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9);
|
||||
}
|
||||
|
||||
dynamicallyCreateComponent(directiveBinding, injector:Injector) {
|
||||
this._shadowDomAppInjector = injector;
|
||||
this._dynamicallyCreatedComponentBinding = directiveBinding;
|
||||
_createShadowDomAppInjector(componentDirective:DirectiveBinding, appInjector:Injector) {
|
||||
var shadowDomAppInjector = null;
|
||||
|
||||
// shadowDomAppInjector
|
||||
var injectables = componentDirective.resolvedInjectables;
|
||||
if (isPresent(injectables)) {
|
||||
shadowDomAppInjector = appInjector.createChildFromResolved(injectables);
|
||||
} else {
|
||||
shadowDomAppInjector = appInjector;
|
||||
}
|
||||
return shadowDomAppInjector;
|
||||
}
|
||||
|
||||
dynamicallyCreateComponent(componentDirective:DirectiveBinding, parentInjector:Injector) {
|
||||
this._shadowDomAppInjector = this._createShadowDomAppInjector(componentDirective, parentInjector);
|
||||
this._dynamicallyCreatedComponentBinding = componentDirective;
|
||||
this._dynamicallyCreatedComponent = this._new(this._dynamicallyCreatedComponentBinding);
|
||||
return this._dynamicallyCreatedComponent;
|
||||
}
|
||||
|
|
|
@ -163,21 +163,7 @@ export class AppViewManagerUtils {
|
|||
}
|
||||
var annotation = this._metadataReader.read(componentBinding.token).annotation;
|
||||
var componentDirective = eli.DirectiveBinding.createFromBinding(componentBinding, annotation);
|
||||
var shadowDomAppInjector = this._createShadowDomAppInjector(componentDirective, injector);
|
||||
elementInjector.dynamicallyCreateComponent(componentDirective, shadowDomAppInjector);
|
||||
}
|
||||
|
||||
_createShadowDomAppInjector(componentDirective, appInjector) {
|
||||
var shadowDomAppInjector = null;
|
||||
|
||||
// shadowDomAppInjector
|
||||
var injectables = componentDirective.resolvedInjectables;
|
||||
if (isPresent(injectables)) {
|
||||
shadowDomAppInjector = appInjector.createChildFromResolved(injectables);
|
||||
} else {
|
||||
shadowDomAppInjector = appInjector;
|
||||
}
|
||||
return shadowDomAppInjector;
|
||||
elementInjector.dynamicallyCreateComponent(componentDirective, injector);
|
||||
}
|
||||
|
||||
_hydrateView(view:viewModule.AppView, appInjector:Injector, hostElementInjector:eli.ElementInjector, context: Object, parentLocals:Locals) {
|
||||
|
@ -194,14 +180,7 @@ export class AppViewManagerUtils {
|
|||
for (var i = 0; i < binders.length; ++i) {
|
||||
var elementInjector = view.elementInjectors[i];
|
||||
if (isPresent(elementInjector)) {
|
||||
var componentDirective = view.proto.elementBinders[i].componentDirective;
|
||||
var shadowDomAppInjector = null;
|
||||
if (isPresent(componentDirective)) {
|
||||
shadowDomAppInjector = this._createShadowDomAppInjector(componentDirective, appInjector);
|
||||
} else {
|
||||
shadowDomAppInjector = null;
|
||||
}
|
||||
elementInjector.instantiateDirectives(appInjector, hostElementInjector, shadowDomAppInjector, view.preBuiltObjects[i]);
|
||||
elementInjector.instantiateDirectives(appInjector, hostElementInjector, view.preBuiltObjects[i]);
|
||||
this._setUpEventEmitters(view, elementInjector, i);
|
||||
|
||||
// The exporting of $implicit is a special case. Since multiple elements will all export
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, SpyObject, proxy, el} from 'angular2/test_lib';
|
||||
import {isBlank, isPresent, IMPLEMENTS} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper, List, StringMapWrapper, iterateListLike} from 'angular2/src/facade/collection';
|
||||
import {ProtoElementInjector, PreBuiltObjects, DirectiveBinding, TreeNode}
|
||||
import {ProtoElementInjector, ElementInjector, PreBuiltObjects, DirectiveBinding, TreeNode}
|
||||
from 'angular2/src/core/compiler/element_injector';
|
||||
import {Parent, Ancestor} from 'angular2/src/core/annotations_impl/visibility';
|
||||
import {Attribute, Query} from 'angular2/src/core/annotations_impl/di';
|
||||
import {Directive, onDestroy} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Component, Directive, onDestroy} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {bind, Injector} from 'angular2/di';
|
||||
import {Optional, Inject} from 'angular2/src/di/annotations_impl';
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||
|
@ -181,6 +181,30 @@ class TestNode extends TreeNode {
|
|||
}
|
||||
}
|
||||
|
||||
// TypeScript erases interfaces, so it has to be a class
|
||||
class ParentInterface {}
|
||||
|
||||
class ParentComponent extends ParentInterface {
|
||||
}
|
||||
|
||||
class AppDependency {
|
||||
parent:ParentInterface;
|
||||
|
||||
constructor(p:ParentInterface) {
|
||||
this.parent = p;
|
||||
}
|
||||
}
|
||||
|
||||
class ChildComponent {
|
||||
parent:ParentInterface;
|
||||
appDependency:AppDependency;
|
||||
|
||||
constructor(p:ParentInterface, a:AppDependency) {
|
||||
this.parent = p;
|
||||
this.appDependency = a;
|
||||
}
|
||||
}
|
||||
|
||||
export function main() {
|
||||
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null);
|
||||
var appInjector = Injector.resolveAndCreate([]);
|
||||
|
@ -195,48 +219,57 @@ export function main() {
|
|||
return [lookupName(tree), children];
|
||||
}
|
||||
|
||||
function injector(bindings, lightDomAppInjector = null, shadowDomAppInjector = null, preBuiltObjects = null, attributes = null) {
|
||||
function injector(bindings, lightDomAppInjector = null,
|
||||
isComponent:bool = false, preBuiltObjects = null, attributes = null) {
|
||||
if (isBlank(lightDomAppInjector)) lightDomAppInjector = appInjector;
|
||||
|
||||
var proto = new ProtoElementInjector(null, 0, bindings, isPresent(shadowDomAppInjector));
|
||||
var proto = new ProtoElementInjector(null, 0, bindings, isComponent);
|
||||
proto.attributes = attributes;
|
||||
var inj = proto.instantiate(null);
|
||||
var preBuilt = isPresent(preBuiltObjects) ? preBuiltObjects : defaultPreBuiltObjects;
|
||||
var preBuilt = isPresent(preBuiltObjects)
|
||||
? preBuiltObjects
|
||||
: defaultPreBuiltObjects;
|
||||
|
||||
inj.instantiateDirectives(lightDomAppInjector, null, shadowDomAppInjector, preBuilt);
|
||||
inj.instantiateDirectives(lightDomAppInjector, null, preBuilt);
|
||||
return inj;
|
||||
}
|
||||
|
||||
function parentChildInjectors(parentBindings, childBindings, parentPreBuildObjects = null) {
|
||||
function parentChildInjectors(
|
||||
parentBindings,
|
||||
childBindings,
|
||||
parentPreBuildObjects = null,
|
||||
isParentComponent:bool = false) {
|
||||
if (isBlank(parentPreBuildObjects)) parentPreBuildObjects = defaultPreBuiltObjects;
|
||||
|
||||
var inj = Injector.resolveAndCreate([]);
|
||||
|
||||
var protoParent = new ProtoElementInjector(null, 0, parentBindings);
|
||||
var protoParent = new ProtoElementInjector(null, 0, parentBindings, isParentComponent);
|
||||
var parent = protoParent.instantiate(null);
|
||||
|
||||
parent.instantiateDirectives(inj, null, null, parentPreBuildObjects);
|
||||
parent.instantiateDirectives(inj, null, parentPreBuildObjects);
|
||||
|
||||
var protoChild = new ProtoElementInjector(protoParent, 1, childBindings, false, 1);
|
||||
var child = protoChild.instantiate(parent);
|
||||
child.instantiateDirectives(inj, null, null, defaultPreBuiltObjects);
|
||||
child.instantiateDirectives(inj, null, defaultPreBuiltObjects);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
function hostShadowInjectors(hostBindings, shadowBindings, hostPreBuildObjects = null) {
|
||||
if (isBlank(hostPreBuildObjects)) hostPreBuildObjects = defaultPreBuiltObjects;
|
||||
|
||||
function hostShadowInjectors(
|
||||
hostBindings:List,
|
||||
shadowBindings:List,
|
||||
isParentComponent:bool = true,
|
||||
isChildComponent:bool = false):ElementInjector {
|
||||
var inj = Injector.resolveAndCreate([]);
|
||||
var shadowInj = inj.resolveAndCreateChild([]);
|
||||
|
||||
var protoParent = new ProtoElementInjector(null, 0, hostBindings, true);
|
||||
var protoParent = new ProtoElementInjector(null, 0, hostBindings, isParentComponent);
|
||||
var host = protoParent.instantiate(null);
|
||||
host.instantiateDirectives(inj, null, shadowInj, hostPreBuildObjects);
|
||||
host.instantiateDirectives(inj, null, defaultPreBuiltObjects);
|
||||
|
||||
var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false, 1);
|
||||
var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings,
|
||||
isChildComponent, 1);
|
||||
var shadow = protoChild.instantiate(null);
|
||||
shadow.instantiateDirectives(shadowInj, host, null, null);
|
||||
shadow.instantiateDirectives(host.getShadowDomAppInjector(), host, null);
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
@ -455,13 +488,14 @@ export function main() {
|
|||
|
||||
it("should instantiate directives that depend on pre built objects", function () {
|
||||
var protoView = new AppProtoView(null, null, null, null, null);
|
||||
var inj = injector([NeedsProtoViewRef], null, null, new PreBuiltObjects(null, null, protoView));
|
||||
var inj = injector([NeedsProtoViewRef], null, false, new PreBuiltObjects(null, null, protoView));
|
||||
|
||||
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on the containing component", function () {
|
||||
var shadow = hostShadowInjectors([SimpleDirective], [NeedsDirective]);
|
||||
var directiveBinding = DirectiveBinding.createFromType(SimpleDirective, new Component());
|
||||
var shadow = hostShadowInjectors([directiveBinding], [NeedsDirective]);
|
||||
|
||||
var d = shadow.get(NeedsDirective);
|
||||
expect(d).toBeAnInstanceOf(NeedsDirective);
|
||||
|
@ -469,16 +503,24 @@ export function main() {
|
|||
});
|
||||
|
||||
it("should not instantiate directives that depend on other directives in the containing component's ElementInjector", () => {
|
||||
var directiveBinding = DirectiveBinding
|
||||
.createFromType(SomeOtherDirective, new Component());
|
||||
expect(() => {
|
||||
hostShadowInjectors([SomeOtherDirective, SimpleDirective], [NeedsDirective]);
|
||||
}).toThrowError('No provider for SimpleDirective! (NeedsDirective -> SimpleDirective)')
|
||||
hostShadowInjectors(
|
||||
[directiveBinding, SimpleDirective],
|
||||
[NeedsDirective]);
|
||||
}).toThrowError('No provider for SimpleDirective! (NeedsDirective -> SimpleDirective)');
|
||||
});
|
||||
|
||||
it("should instantiate component directives that depend on app services in the shadow app injector", () => {
|
||||
var shadowAppInjector = Injector.resolveAndCreate([
|
||||
var directiveAnnotation = new Component({
|
||||
injectables: [
|
||||
bind("service").toValue("service")
|
||||
]);
|
||||
var inj = injector([NeedsService], null, shadowAppInjector);
|
||||
]
|
||||
});
|
||||
var componentDirective = DirectiveBinding.createFromType(
|
||||
NeedsService, directiveAnnotation);
|
||||
var inj = injector([componentDirective], null, true);
|
||||
|
||||
var d = inj.get(NeedsService);
|
||||
expect(d).toBeAnInstanceOf(NeedsService);
|
||||
|
@ -486,11 +528,14 @@ export function main() {
|
|||
});
|
||||
|
||||
it("should not instantiate other directives that depend on app services in the shadow app injector", () => {
|
||||
var shadowAppInjector = Injector.resolveAndCreate([
|
||||
var directiveAnnotation = new Component({
|
||||
injectables: [
|
||||
bind("service").toValue("service")
|
||||
]);
|
||||
]
|
||||
});
|
||||
var componentDirective = DirectiveBinding.createFromType(SimpleDirective, directiveAnnotation);
|
||||
expect(() => {
|
||||
injector([SomeOtherDirective, NeedsService], null, shadowAppInjector);
|
||||
injector([componentDirective, NeedsService], null);
|
||||
}).toThrowError('No provider for service! (NeedsService -> service)');
|
||||
});
|
||||
|
||||
|
@ -515,7 +560,7 @@ export function main() {
|
|||
it("should not return parent's directives on self", function () {
|
||||
expect(() => {
|
||||
injector([SimpleDirective, NeedDirectiveFromParent]);
|
||||
}).toThrowError();
|
||||
}).toThrowError(new RegExp("No provider for SimpleDirective"));
|
||||
});
|
||||
|
||||
it("should get directives from ancestor", function () {
|
||||
|
@ -576,6 +621,31 @@ export function main() {
|
|||
inj.clearDirectives();
|
||||
expect(destroy.onDestroyCounter).toBe(1);
|
||||
});
|
||||
|
||||
it("should publish component to its children via app injector when requested", function() {
|
||||
var parentDirective = new Component({
|
||||
selector: 'parent',
|
||||
publishAs: [ParentInterface]
|
||||
});
|
||||
var parentBinding = DirectiveBinding.createFromType(ParentComponent, parentDirective);
|
||||
|
||||
var childDirective = new Component({
|
||||
selector: 'child',
|
||||
injectables: [AppDependency]
|
||||
});
|
||||
var childBinding = DirectiveBinding.createFromType(ChildComponent, childDirective);
|
||||
|
||||
var child = hostShadowInjectors([parentBinding], [childBinding], true, true);
|
||||
var d = child.get(ChildComponent);
|
||||
|
||||
// Verify that the child component can inject parent via interface binding
|
||||
expect(d).toBeAnInstanceOf(ChildComponent);
|
||||
expect(d.parent).toBeAnInstanceOf(ParentComponent);
|
||||
|
||||
// Verify that the binding is available down the dependency tree
|
||||
expect(d.appDependency.parent).toBeAnInstanceOf(ParentComponent);
|
||||
expect(d.parent).toBe(d.appDependency.parent);
|
||||
});
|
||||
});
|
||||
|
||||
describe("dynamicallyCreateComponent", () => {
|
||||
|
@ -601,25 +671,35 @@ export function main() {
|
|||
var shadowDomInj = shadowDomProtoInjector.instantiate(null);
|
||||
|
||||
expect(() =>
|
||||
shadowDomInj.instantiateDirectives(appInjector, injWithDynamicallyLoadedComponent,null, defaultPreBuiltObjects)).
|
||||
shadowDomInj.instantiateDirectives(appInjector, injWithDynamicallyLoadedComponent, defaultPreBuiltObjects)).
|
||||
toThrowError(new RegExp("No provider for SimpleDirective"));
|
||||
});
|
||||
|
||||
it("should not inject the dynamically-loaded component into directives on the same element", () => {
|
||||
var proto = new ProtoElementInjector(null, 0, [NeedsDirective], false);
|
||||
var dynamicComp = DirectiveBinding.createFromType(SomeOtherDirective, new Component());
|
||||
var proto = new ProtoElementInjector(null, 0, [dynamicComp, NeedsDirective], true);
|
||||
var inj = proto.instantiate(null);
|
||||
inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), null);
|
||||
inj.dynamicallyCreateComponent(
|
||||
DirectiveBinding.createFromType(SimpleDirective, null), null);
|
||||
|
||||
expect(() => inj.instantiateDirectives(null, null, null, null)).toThrowError();
|
||||
var error = null;
|
||||
try {
|
||||
inj.instantiateDirectives(Injector.resolveAndCreate([]), null, null);
|
||||
} catch(e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("No provider for SimpleDirective! (NeedsDirective -> SimpleDirective)");
|
||||
});
|
||||
|
||||
it("should inject the dynamically-loaded component into the children of the dynamically-loaded component", () => {
|
||||
var componentDirective = DirectiveBinding.createFromType(SimpleDirective, null);
|
||||
var injWithDynamicallyLoadedComponent = injector([]);
|
||||
injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), null);
|
||||
injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(componentDirective, null);
|
||||
|
||||
var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false);
|
||||
var shadowDomInjector = shadowDomProtoInjector.instantiate(null);
|
||||
shadowDomInjector.instantiateDirectives(appInjector, injWithDynamicallyLoadedComponent, null, defaultPreBuiltObjects);
|
||||
shadowDomInjector.instantiateDirectives(appInjector, injWithDynamicallyLoadedComponent, defaultPreBuiltObjects);
|
||||
|
||||
expect(shadowDomInjector.get(NeedDirectiveFromAncestor)).toBeAnInstanceOf(NeedDirectiveFromAncestor);
|
||||
expect(shadowDomInjector.get(NeedDirectiveFromAncestor).dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
|
@ -640,7 +720,7 @@ export function main() {
|
|||
expect(inj.getDynamicallyLoadedComponent()).toBe(null);
|
||||
expect(dir.onDestroyCounter).toBe(1);
|
||||
|
||||
inj.instantiateDirectives(null, null, null, null);
|
||||
inj.instantiateDirectives(null, null, null);
|
||||
|
||||
expect(inj.getDynamicallyLoadedComponent()).toBe(null);
|
||||
});
|
||||
|
@ -659,7 +739,7 @@ export function main() {
|
|||
MapWrapper.set(attributes, 'type', 'text');
|
||||
MapWrapper.set(attributes, 'title', '');
|
||||
|
||||
var inj = injector([NeedsAttribute], null, null, null, attributes);
|
||||
var inj = injector([NeedsAttribute], null, false, null, attributes);
|
||||
var needsAttribute = inj.get(NeedsAttribute);
|
||||
|
||||
expect(needsAttribute.typeAttribute).toEqual('text');
|
||||
|
@ -671,7 +751,7 @@ export function main() {
|
|||
var attributes = MapWrapper.create();
|
||||
MapWrapper.set(attributes, 'foo', 'bar');
|
||||
|
||||
var inj = injector([NeedsAttributeNoType], null, null, null, attributes);
|
||||
var inj = injector([NeedsAttributeNoType], null, false, null, attributes);
|
||||
var needsAttribute = inj.get(NeedsAttributeNoType);
|
||||
|
||||
expect(needsAttribute.fooAttribute).toEqual('bar');
|
||||
|
@ -690,7 +770,7 @@ export function main() {
|
|||
var childView = new DummyView();
|
||||
childView.changeDetector = cd;
|
||||
view.componentChildViews = [childView];
|
||||
var inj = injector([NeedsChangeDetectorRef], null, null, new PreBuiltObjects(null, view, null));
|
||||
var inj = injector([NeedsChangeDetectorRef], null, false, new PreBuiltObjects(null, view, null));
|
||||
|
||||
expect(inj.get(NeedsChangeDetectorRef).changeDetectorRef).toBe(cd.ref);
|
||||
});
|
||||
|
@ -702,7 +782,7 @@ export function main() {
|
|||
|
||||
it("should inject ProtoViewRef", function () {
|
||||
var protoView = new AppProtoView(null, null, null, null, null);
|
||||
var inj = injector([NeedsProtoViewRef], null, null, new PreBuiltObjects(null, null, protoView));
|
||||
var inj = injector([NeedsProtoViewRef], null, false, new PreBuiltObjects(null, null, protoView));
|
||||
|
||||
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
|
||||
});
|
||||
|
@ -730,12 +810,12 @@ export function main() {
|
|||
}
|
||||
|
||||
it('should be injectable', () => {
|
||||
var inj = injector([NeedsQuery], null, null, preBuildObjects);
|
||||
var inj = injector([NeedsQuery], null, false, preBuildObjects);
|
||||
expect(inj.get(NeedsQuery).query).toBeAnInstanceOf(QueryList);
|
||||
});
|
||||
|
||||
it('should contain directives on the same injector', () => {
|
||||
var inj = injector([NeedsQuery, CountingDirective], null, null, preBuildObjects);
|
||||
var inj = injector([NeedsQuery, CountingDirective], null, false, preBuildObjects);
|
||||
|
||||
expectDirectives(inj.get(NeedsQuery).query, CountingDirective, [0]);
|
||||
});
|
||||
|
@ -756,8 +836,8 @@ export function main() {
|
|||
|
||||
var parent = protoParent.instantiate(null);
|
||||
var child = protoChild.instantiate(parent);
|
||||
parent.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
child.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
parent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
child.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
|
||||
expectDirectives(parent.get(NeedsQuery).query, CountingDirective, [0,1]);
|
||||
});
|
||||
|
@ -768,8 +848,8 @@ export function main() {
|
|||
|
||||
var parent = protoParent.instantiate(null);
|
||||
var child = protoChild.instantiate(parent);
|
||||
parent.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
child.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
parent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
child.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
|
||||
child.unlink();
|
||||
|
||||
|
@ -785,9 +865,9 @@ export function main() {
|
|||
var child1 = protoChild1.instantiate(parent);
|
||||
var child2 = protoChild2.instantiate(parent);
|
||||
|
||||
parent.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
child1.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
child2.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
parent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
child1.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
child2.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
|
||||
child1.unlink();
|
||||
child1.link(parent);
|
||||
|
@ -805,9 +885,9 @@ export function main() {
|
|||
var child1 = protoChild1.instantiate(parent);
|
||||
var child2 = protoChild2.instantiate(parent);
|
||||
|
||||
parent.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
child1.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
child2.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
parent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
child1.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
child2.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
|
||||
child2.unlink();
|
||||
child2.linkAfter(parent, null);
|
||||
|
@ -825,9 +905,9 @@ export function main() {
|
|||
var parent = protoParent.instantiate(grandParent);
|
||||
var child = protoChild.instantiate(parent);
|
||||
|
||||
grandParent.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
parent.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
child.instantiateDirectives(Injector.resolveAndCreate([]), null, null, preBuildObjects);
|
||||
grandParent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
parent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
child.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
|
||||
|
||||
var queryList1 = grandParent.get(NeedsQuery).query;
|
||||
var queryList2 = parent.get(NeedsQuery).query;
|
||||
|
|
|
@ -26,6 +26,7 @@ import {PipeRegistry, defaultPipeRegistry,
|
|||
ChangeDetection, DynamicChangeDetection, Pipe, ChangeDetectorRef, ON_PUSH} from 'angular2/change_detection';
|
||||
|
||||
import {Directive, Component} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
import {Parent, Ancestor} from 'angular2/src/core/annotations_impl/visibility';
|
||||
import {Attribute} from 'angular2/src/core/annotations_impl/di';
|
||||
|
@ -857,6 +858,70 @@ export function main() {
|
|||
}
|
||||
});
|
||||
|
||||
describe('dependency injection', () => {
|
||||
|
||||
it('should publish parent component to shadow DOM via publishAs',
|
||||
inject([TestBed, AsyncTestCompleter, Compiler], (tb, async, compiler) => {
|
||||
tb.overrideView(MyComp, new View({
|
||||
template: `<parent></parent>`,
|
||||
directives: [ParentComponent]
|
||||
}));
|
||||
|
||||
tb.createView(MyComp).then((view) => {
|
||||
view.detectChanges();
|
||||
expect(view.rootNodes).toHaveText(
|
||||
'Parent,Parent');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should override parent bindings via publishAs',
|
||||
inject([TestBed, AsyncTestCompleter, Compiler], (tb, async, compiler) => {
|
||||
tb.overrideView(MyComp, new View({
|
||||
template: `<recursive-parent></recursive-parent>`,
|
||||
directives: [RecursiveParentComponent]
|
||||
}));
|
||||
|
||||
tb.createView(MyComp).then((view) => {
|
||||
view.detectChanges();
|
||||
expect(view.rootNodes).toHaveText(
|
||||
'ParentInterface,RecursiveParent,RecursiveParent');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
// [DynamicComponentLoader] already supports providing a custom
|
||||
// injector as an argument to `loadIntoExistingLocation`, which should
|
||||
// be used instead of `publishAs`.
|
||||
//
|
||||
// Conceptually dynamically loaded components are loaded _instead_ of
|
||||
// the dynamic component itself. The dynamic component does not own the
|
||||
// shadow DOM. It's the loaded component that creates that shadow DOM.
|
||||
it('should not publish into dynamically instantiated components via publishAs',
|
||||
inject([TestBed, AsyncTestCompleter, Compiler], (tb, async, compiler) => {
|
||||
tb.overrideView(MyComp, new View({
|
||||
template: `<dynamic-parent #cmp></dynamic-parent>`,
|
||||
directives: [DynamicParentComponent]
|
||||
}));
|
||||
|
||||
tb.createView(MyComp).then((view) => {
|
||||
view.detectChanges();
|
||||
var comp = view.rawView.locals.get("cmp");
|
||||
PromiseWrapper.then(comp.done,
|
||||
(value) => {
|
||||
throw new BaseException(`Expected to throw error, but got value ${value}`);
|
||||
},
|
||||
(err) => {
|
||||
expect(err.message)
|
||||
.toEqual('No provider for ParentInterface! (ChildComponent -> ParentInterface)');
|
||||
async.done();
|
||||
}
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1265,3 +1330,84 @@ class NeedsPublicApi {
|
|||
expect(api instanceof PrivateImpl).toBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
class ParentInterface {
|
||||
message:String;
|
||||
constructor() {
|
||||
this.message = 'ParentInterface';
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'parent',
|
||||
publishAs: [ParentInterface]
|
||||
})
|
||||
@View({
|
||||
template: `<child></child>`,
|
||||
directives: [ChildComponent]
|
||||
})
|
||||
class ParentComponent extends ParentInterface {
|
||||
message:String;
|
||||
constructor() {
|
||||
super();
|
||||
this.message = 'Parent';
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
injectables: [ParentInterface],
|
||||
selector: 'recursive-parent',
|
||||
publishAs: [ParentInterface]
|
||||
})
|
||||
@View({
|
||||
template: `{{parentService.message}},<child></child>`,
|
||||
directives: [ChildComponent]
|
||||
})
|
||||
class RecursiveParentComponent extends ParentInterface {
|
||||
parentService:ParentInterface;
|
||||
message:String;
|
||||
constructor(parentService:ParentInterface) {
|
||||
super();
|
||||
this.message = 'RecursiveParent';
|
||||
this.parentService = parentService;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'dynamic-parent',
|
||||
publishAs: [ParentInterface]
|
||||
})
|
||||
class DynamicParentComponent extends ParentInterface {
|
||||
message:String;
|
||||
done;
|
||||
constructor(loader:DynamicComponentLoader, location:ElementRef) {
|
||||
super();
|
||||
this.message = 'DynamicParent';
|
||||
this.done = loader.loadIntoExistingLocation(ChildComponent, location);
|
||||
}
|
||||
}
|
||||
|
||||
class AppDependency {
|
||||
parent:ParentInterface;
|
||||
|
||||
constructor(p:ParentInterface) {
|
||||
this.parent = p;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'child',
|
||||
injectables: [AppDependency]
|
||||
})
|
||||
@View({
|
||||
template: `<div>{{parent.message}}</div>,<div>{{appDependency.parent.message}}</div>`
|
||||
})
|
||||
class ChildComponent {
|
||||
parent:ParentInterface;
|
||||
appDependency:AppDependency;
|
||||
|
||||
constructor(p:ParentInterface, a:AppDependency) {
|
||||
this.parent = p;
|
||||
this.appDependency = a;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,14 +21,14 @@ export function main() {
|
|||
function instantiate () {
|
||||
for (var i = 0; i < iterations; ++i) {
|
||||
var ei = proto.instantiate(null);
|
||||
ei.instantiateDirectives(appInjector, null, null, null);
|
||||
ei.instantiateDirectives(appInjector, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
function instantiateDirectives () {
|
||||
for (var i = 0; i < iterations; ++i) {
|
||||
elementInjector.clearDirectives();
|
||||
elementInjector.instantiateDirectives(appInjector, null, null, null);
|
||||
elementInjector.instantiateDirectives(appInjector, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue