diff --git a/lib/core/directive.dart b/lib/core/directive.dart index 68629b974..6b2f6cafc 100644 --- a/lib/core/directive.dart +++ b/lib/core/directive.dart @@ -56,7 +56,27 @@ abstract class NgAnnotation { * direct children of the current DOM element. */ final String visibility; - final List publishTypes; + + /** + * A directive/component class can publish types by using a function which + * creates a module. The types specified in module will then be available + * using directive's/component's injector. + * + * Example: + * + * @NgDirective(selector: '[foo]', module: FooDirective.initModule) + * FooDirective { + * static initModule() => new Module()..type(SomeTypeA); + * } + * + * When specifying types, factories or values in the module, notice that + * `Visibility` function maps to `visibility` parameter in [NgDirective] as + * follows: + * * [NgDirective.LOCAL_VISIBILITY] -> [ElementBinder.localVisibility] + * * [NgDirective.CHILDREN_VISIBILITY] -> `null` + * * [NgDirective.DIRECT_CHILDREN_VISIBILITY] -> [ElementBinder.directChildrenVisibility] + */ + final Function module; /** * Use map to define the mapping of DOM attributes to fields. @@ -139,7 +159,7 @@ abstract class NgAnnotation { this.selector, this.children: NgAnnotation.COMPILE_CHILDREN, this.visibility: NgDirective.LOCAL_VISIBILITY, - this.publishTypes: const [], + this.module, this.map: const {}, this.exportExpressions: const [], this.exportExpressionAttrs: const [] @@ -213,18 +233,18 @@ class NgComponent extends NgAnnotation { this.applyAuthorStyles, this.resetStyleInheritance, this.publishAs, + module, map, selector, visibility, - publishTypes : const [], exportExpressions, exportExpressionAttrs}) : _cssUrls = cssUrl, super(selector: selector, children: NgAnnotation.COMPILE_CHILDREN, visibility: visibility, - publishTypes: publishTypes, map: map, + module: module, exportExpressions: exportExpressions, exportExpressionAttrs: exportExpressionAttrs); @@ -241,9 +261,9 @@ class NgComponent extends NgAnnotation { resetStyleInheritance: resetStyleInheritance, publishAs: publishAs, map: newMap, + module: module, selector: selector, visibility: visibility, - publishTypes: publishTypes, exportExpressions: exportExpressions, exportExpressionAttrs: exportExpressionAttrs); } @@ -271,15 +291,15 @@ class NgDirective extends NgAnnotation { const NgDirective({children: NgAnnotation.COMPILE_CHILDREN, map, selector, + module, visibility, - publishTypes : const [], exportExpressions, exportExpressionAttrs}) : super(selector: selector, children: children, visibility: visibility, - publishTypes: publishTypes, map: map, + module: module, exportExpressions: exportExpressions, exportExpressionAttrs: exportExpressionAttrs); @@ -287,9 +307,9 @@ class NgDirective extends NgAnnotation { new NgDirective( children: children, map: newMap, + module: module, selector: selector, visibility: visibility, - publishTypes: publishTypes, exportExpressions: exportExpressions, exportExpressionAttrs: exportExpressionAttrs); } @@ -327,17 +347,17 @@ class NgController extends NgDirective { children: NgAnnotation.COMPILE_CHILDREN, this.publishAs, map, + module, selector, visibility, - publishTypes : const [], exportExpressions, exportExpressionAttrs }) : super(selector: selector, children: children, visibility: visibility, - publishTypes: publishTypes, map: map, + module: module, exportExpressions: exportExpressions, exportExpressionAttrs: exportExpressionAttrs); @@ -345,10 +365,10 @@ class NgController extends NgDirective { new NgController( children: children, publishAs: publishAs, + module: module, map: newMap, selector: selector, visibility: visibility, - publishTypes: publishTypes, exportExpressions: exportExpressions, exportExpressionAttrs: exportExpressionAttrs); } diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart index b40ac5127..4de97f840 100644 --- a/lib/core_dom/element_binder.dart +++ b/lib/core_dom/element_binder.dart @@ -78,16 +78,16 @@ class ElementBinder { _usableDirectiveRefs.isNotEmpty || onEvents.isNotEmpty; // DI visibility strategy allowing node-local visibility. - static final Function _elementOnly = (Injector requesting, Injector defining) { + static final Function localVisibility = (Injector requesting, Injector defining) { if (requesting.name == _SHADOW) requesting = requesting.parent; return identical(requesting, defining); }; // DI visibility strategy allowing visibility from direct child into parent. - static final Function _elementDirectChildren = + static final Function directChildrenVisibility = (Injector requesting, Injector defining) { if (requesting.name == _SHADOW) requesting = requesting.parent; - return _elementOnly(requesting, defining) || + return localVisibility(requesting, defining) || identical(requesting.parent, defining); }; @@ -117,7 +117,7 @@ class ElementBinder { directiveRefs.forEach((DirectiveRef ref) { NgAnnotation annotation = ref.annotation; - var visibility = _elementOnly; + var visibility = localVisibility; if (ref.annotation is NgController) { scope = scope.createChild(new PrototypeMap(scope.context)); nodeModule.value(Scope, scope); @@ -128,7 +128,7 @@ class ElementBinder { visibility = null; break; case NgDirective.DIRECT_CHILDREN_VISIBILITY: - visibility = _elementDirectChildren; + visibility = directChildrenVisibility; break; } @@ -173,9 +173,9 @@ class ElementBinder { } else { nodeModule.type(ref.type, visibility: visibility); } - for (var publishType in ref.annotation.publishTypes) { - nodeModule.factory(publishType, (Injector injector) => - injector.get(ref.type), visibility: visibility); + + if (ref.annotation.module != null) { + nodeModule.install(ref.annotation.module()); } if (annotation.children == NgAnnotation.TRANSCLUDE_CHILDREN) { // Currently, transclude is only supported for NgDirective. diff --git a/lib/directive/ng_form.dart b/lib/directive/ng_form.dart index 8e02ff549..62d0ec753 100644 --- a/lib/directive/ng_form.dart +++ b/lib/directive/ng_form.dart @@ -7,22 +7,24 @@ part of angular.directive; */ @NgDirective( selector: 'form', - publishTypes : const [NgControl], + module: NgForm.initModule, visibility: NgDirective.CHILDREN_VISIBILITY) @NgDirective( selector: 'fieldset', - publishTypes : const [NgControl], + module: NgForm.initModule, visibility: NgDirective.CHILDREN_VISIBILITY) @NgDirective( selector: '.ng-form', - publishTypes : const [NgControl], + module: NgForm.initModule, visibility: NgDirective.CHILDREN_VISIBILITY) @NgDirective( selector: '[ng-form]', - publishTypes : const [NgControl], + module: NgForm.initModule, map: const { 'ng-form': '@name' }, visibility: NgDirective.CHILDREN_VISIBILITY) class NgForm extends NgControl { + static initModule() => new Module()..factory(NgControl, (i) => i.get(NgForm)); + final Scope _scope; /** diff --git a/lib/routing/ng_bind_route.dart b/lib/routing/ng_bind_route.dart index a5b2d38d1..39b21bc20 100644 --- a/lib/routing/ng_bind_route.dart +++ b/lib/routing/ng_bind_route.dart @@ -24,14 +24,17 @@ part of angular.routing; */ @NgDirective( visibility: NgDirective.CHILDREN_VISIBILITY, - publishTypes: const [RouteProvider], selector: '[ng-bind-route]', + module: NgBindRouteDirective.initModule, map: const {'ng-bind-route': '@routeName'}) class NgBindRouteDirective implements RouteProvider { Router _router; String routeName; Injector _injector; + static initModule() => new Module()..factory(RouteProvider, + (i) => i.get(NgBindRouteDirective)); + // We inject NgRoutingHelper to force initialization of routing. NgBindRouteDirective(this._router, this._injector, NgRoutingHelper _); diff --git a/lib/routing/ng_view.dart b/lib/routing/ng_view.dart index 22f6e03ee..a846b7c0d 100644 --- a/lib/routing/ng_view.dart +++ b/lib/routing/ng_view.dart @@ -57,9 +57,12 @@ part of angular.routing; */ @NgDirective( selector: 'ng-view', - publishTypes: const [RouteProvider], + module: NgViewDirective.initModule, visibility: NgDirective.CHILDREN_VISIBILITY) class NgViewDirective implements NgDetachAware, RouteProvider { + static initModule() => + new Module()..factory(RouteProvider, (i) => i.get(RouteProvider)); + final NgRoutingHelper locationService; final ViewCache viewCache; final Injector injector; diff --git a/test/core/core_directive_spec.dart b/test/core/core_directive_spec.dart index 792d6d28a..b55b598f0 100644 --- a/test/core/core_directive_spec.dart +++ b/test/core/core_directive_spec.dart @@ -19,7 +19,7 @@ void main() { expect(annotation.selector).toEqual('annotated-io'); expect(annotation.visibility).toEqual(NgDirective.LOCAL_VISIBILITY); expect(annotation.exportExpressions).toEqual(['exportExpressions']); - expect(annotation.publishTypes).toEqual([String]); + expect(annotation.module).toEqual(AnnotatedIoComponent.initModule); expect(annotation.template).toEqual('template'); expect(annotation.templateUrl).toEqual('templateUrl'); expect(annotation.cssUrls).toEqual(['cssUrls']); @@ -86,13 +86,17 @@ class NullParser implements Parser { applyAuthorStyles: true, resetStyleInheritance: true, publishAs: 'ctrl', - publishTypes: const [String], + module: AnnotatedIoComponent.initModule, visibility: NgDirective.LOCAL_VISIBILITY, exportExpressions: const ['exportExpressions'], map: const { 'foo': '=>foo' }) class AnnotatedIoComponent { + static initModule() => new Module()..factory(String, + (i) => i.get(AnnotatedIoComponent), + visibility: ElementBinder.localVisibility); + AnnotatedIoComponent(Scope scope) { scope.rootScope.context['ioComponent'] = this; } diff --git a/test/core_dom/compiler_spec.dart b/test/core_dom/compiler_spec.dart index 27783744b..1ee821d4e 100644 --- a/test/core_dom/compiler_spec.dart +++ b/test/core_dom/compiler_spec.dart @@ -727,9 +727,10 @@ class PublishTypesDirectiveSuperType { @NgDirective( selector: '[publish-types]', - publishTypes: const [PublishTypesDirectiveSuperType]) + module: PublishTypesAttrDirective.initModule) class PublishTypesAttrDirective implements PublishTypesDirectiveSuperType { static Injector _injector; + static initModule() => new Module()..factory(PublishTypesDirectiveSuperType, (injector) => injector.get(PublishTypesAttrDirective)); PublishTypesAttrDirective(Injector injector) { _injector = injector; }