feat(di): components can self-publish via publishAs

This commit is contained in:
yjbanov
2015-05-08 16:13:00 -07:00
parent abc8878547
commit 1a0da11e55
6 changed files with 366 additions and 87 deletions

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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