diff --git a/modules/angular2/src/core/annotations_impl/annotations.js b/modules/angular2/src/core/annotations_impl/annotations.js
index 6b88d1a282..6a5e4a7798 100644
--- a/modules/angular2/src/core/annotations_impl/annotations.js
+++ b/modules/angular2/src/core/annotations_impl/annotations.js
@@ -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: '',
+ * 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;
}
}
diff --git a/modules/angular2/src/core/compiler/element_injector.js b/modules/angular2/src/core/compiler/element_injector.js
index 7784326d65..69322d81cf 100644
--- a/modules/angular2/src/core/compiler/element_injector.js
+++ b/modules/angular2/src/core/compiler/element_injector.js
@@ -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;
}
diff --git a/modules/angular2/src/core/compiler/view_manager_utils.js b/modules/angular2/src/core/compiler/view_manager_utils.js
index 4a203acd13..db3968fda8 100644
--- a/modules/angular2/src/core/compiler/view_manager_utils.js
+++ b/modules/angular2/src/core/compiler/view_manager_utils.js
@@ -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
diff --git a/modules/angular2/test/core/compiler/element_injector_spec.js b/modules/angular2/test/core/compiler/element_injector_spec.js
index cac9aca9b2..8c4a373948 100644
--- a/modules/angular2/test/core/compiler/element_injector_spec.js
+++ b/modules/angular2/test/core/compiler/element_injector_spec.js
@@ -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([
- bind("service").toValue("service")
- ]);
- var inj = injector([NeedsService], null, shadowAppInjector);
+ var directiveAnnotation = new Component({
+ injectables: [
+ bind("service").toValue("service")
+ ]
+ });
+ 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([
- bind("service").toValue("service")
- ]);
+ 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;
diff --git a/modules/angular2/test/core/compiler/integration_spec.js b/modules/angular2/test/core/compiler/integration_spec.js
index 49577a5ded..45f3f7e201 100644
--- a/modules/angular2/test/core/compiler/integration_spec.js
+++ b/modules/angular2/test/core/compiler/integration_spec.js
@@ -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: ``,
+ 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: ``,
+ 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: ``,
+ 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: ``,
+ 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}},`,
+ 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: `
{{parent.message}}
,{{appDependency.parent.message}}
`
+})
+class ChildComponent {
+ parent:ParentInterface;
+ appDependency:AppDependency;
+
+ constructor(p:ParentInterface, a:AppDependency) {
+ this.parent = p;
+ this.appDependency = a;
+ }
+}
diff --git a/modules/benchmarks/src/element_injector/element_injector_benchmark.js b/modules/benchmarks/src/element_injector/element_injector_benchmark.js
index 660db46a93..66517fba21 100644
--- a/modules/benchmarks/src/element_injector/element_injector_benchmark.js
+++ b/modules/benchmarks/src/element_injector/element_injector_benchmark.js
@@ -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);
}
}