Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
feat(NgAnnotation): Use module parameter to publish types.
Browse files Browse the repository at this point in the history
`module` parameter in NgAnnotation (and other annotations that extend
it) can be used to publish types. The change is a bit more wordy
but it is a lot more powerful.

BREAKING CHANGE: `publishTypes` parameter is removed.

@ngdirective(
  publishTypes: [FooInt]
)
class Foo extends FooInt {
}

becomes

@ngdirective(
  module: Foo.module,
  visibility: NgDirective.LOCAL_VISIBILITY
)
class Foo extends FooInt {
  module() => new Module()
    ..factory(FooInt,
              (i) => i.get(Foo),
              visibility: NgDirective.LOCAL_VISIBILITY)
}

Closes #779
  • Loading branch information
mvuksano authored and mhevery committed Apr 2, 2014
1 parent bbef6c2 commit 5ec7e83
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 54 deletions.
67 changes: 52 additions & 15 deletions lib/core/directive.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,32 @@ abstract class NgAnnotation {
* controller can be injected into other directives / components on the
* direct children of the current DOM element.
*/
final String visibility;
final List<Type> publishTypes;
final Visibility visibility;

/**
* A directive/component class can publish types by using a factory
* function to generate a module. The module is then installed into
* the injector at that element. Any types declared in the module than
* become available for injection.
*
* Example:
*
* @NgDirective(
* selector: '[foo]',
* module: FooDirective.moduleFactory)
* FooDirective {
* // We can be static since Module definition does not change.
* static moduleFactory() => new Module()
* ..type(SomeTypeA, visibility: NgDirective.LOCAL_VISIBILITY);
* }
*
* When specifying types, factories or values in the module, notice that
* `Visibility` maps to:
* * [NgDirective.LOCAL_VISIBILITY]
* * [NgDirective.CHILDREN_VISIBILITY]
* * [NgDirective.DIRECT_CHILDREN_VISIBILITY]
*/
final Function module;

/**
* Use map to define the mapping of DOM attributes to fields.
Expand Down Expand Up @@ -139,7 +163,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 []
Expand Down Expand Up @@ -213,18 +237,18 @@ class NgComponent extends NgAnnotation {
this.applyAuthorStyles,
this.resetStyleInheritance,
this.publishAs,
module,
map,
selector,
visibility,
publishTypes : const <Type>[],
exportExpressions,
exportExpressionAttrs})
: _cssUrls = cssUrl,
super(selector: selector,
children: NgAnnotation.COMPILE_CHILDREN,
visibility: visibility,
publishTypes: publishTypes,
map: map,
module: module,
exportExpressions: exportExpressions,
exportExpressionAttrs: exportExpressionAttrs);

Expand All @@ -241,15 +265,28 @@ class NgComponent extends NgAnnotation {
resetStyleInheritance: resetStyleInheritance,
publishAs: publishAs,
map: newMap,
module: module,
selector: selector,
visibility: visibility,
publishTypes: publishTypes,
exportExpressions: exportExpressions,
exportExpressionAttrs: exportExpressionAttrs);
}

RegExp _ATTR_NAME = new RegExp(r'\[([^\]]+)\]$');

const String SHADOW_DOM_INJECTOR_NAME = 'SHADOW_INJECTOR';

_skipShadow(Injector injector)

This comment has been minimized.

Copy link
@vicb

vicb Apr 2, 2014

Contributor

=> should be on this line

This comment has been minimized.

Copy link
@vicb

vicb Apr 2, 2014

Contributor

Would be good to specify the return type

=> injector.name == SHADOW_DOM_INJECTOR_NAME ? injector.parent : injector;

_localVisibility (Injector requesting, Injector defining)

This comment has been minimized.

Copy link
@vicb

vicb Apr 2, 2014

Contributor

same here

=> identical(_skipShadow(requesting), defining);

_directChildrenVisibility(Injector requesting, Injector defining) {

This comment has been minimized.

Copy link
@vicb

vicb Apr 2, 2014

Contributor

return type

requesting = _skipShadow(requesting);
return identical(requesting.parent, defining) || _localVisibility(requesting, defining);
}

/**
* Meta-data marker placed on a class which should act as a directive.
*
Expand All @@ -264,32 +301,32 @@ RegExp _ATTR_NAME = new RegExp(r'\[([^\]]+)\]$');
* * `detach()` - Called on when owning scope is destroyed.
*/
class NgDirective extends NgAnnotation {
static const String LOCAL_VISIBILITY = 'local';
static const String CHILDREN_VISIBILITY = 'children';
static const String DIRECT_CHILDREN_VISIBILITY = 'direct_children';
static const Visibility LOCAL_VISIBILITY = _localVisibility;
static const Visibility CHILDREN_VISIBILITY = null;
static const Visibility DIRECT_CHILDREN_VISIBILITY = _directChildrenVisibility;

const NgDirective({children: NgAnnotation.COMPILE_CHILDREN,
map,
selector,
module,
visibility,
publishTypes : const <Type>[],
exportExpressions,
exportExpressionAttrs})
: super(selector: selector,
children: children,
visibility: visibility,
publishTypes: publishTypes,
map: map,
module: module,
exportExpressions: exportExpressions,
exportExpressionAttrs: exportExpressionAttrs);

NgAnnotation cloneWithNewMap(newMap) =>
new NgDirective(
children: children,
map: newMap,
module: module,
selector: selector,
visibility: visibility,
publishTypes: publishTypes,
exportExpressions: exportExpressions,
exportExpressionAttrs: exportExpressionAttrs);
}
Expand Down Expand Up @@ -327,28 +364,28 @@ class NgController extends NgDirective {
children: NgAnnotation.COMPILE_CHILDREN,
this.publishAs,
map,
module,
selector,
visibility,
publishTypes : const <Type>[],
exportExpressions,
exportExpressionAttrs
})
: super(selector: selector,
children: children,
visibility: visibility,
publishTypes: publishTypes,
map: map,
module: module,
exportExpressions: exportExpressions,
exportExpressionAttrs: exportExpressionAttrs);

NgAnnotation cloneWithNewMap(newMap) =>
new NgController(
children: children,
publishAs: publishAs,
module: module,
map: newMap,
selector: selector,
visibility: visibility,
publishTypes: publishTypes,
exportExpressions: exportExpressions,
exportExpressionAttrs: exportExpressionAttrs);
}
Expand Down
31 changes: 4 additions & 27 deletions lib/core_dom/element_binder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,6 @@ class ElementBinder {
bool get hasDirectivesOrEvents =>
_usableDirectiveRefs.isNotEmpty || onEvents.isNotEmpty;

// DI visibility strategy allowing node-local visibility.
static final Function _elementOnly = (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 =
(Injector requesting, Injector defining) {
if (requesting.name == _SHADOW) requesting = requesting.parent;
return _elementOnly(requesting, defining) ||
identical(requesting.parent, defining);
};

Injector bind(View view, Injector parentInjector, dom.Node node) {
var timerId;
assert((timerId = _perf.startTimer('ng.view.link.setUp', _html(node))) != false);
Expand All @@ -117,21 +103,12 @@ class ElementBinder {

directiveRefs.forEach((DirectiveRef ref) {
NgAnnotation annotation = ref.annotation;
var visibility = _elementOnly;
var visibility = ref.annotation.visibility;
if (ref.annotation is NgController) {
scope = scope.createChild(new PrototypeMap(scope.context));
nodeModule.value(Scope, scope);
}

switch (ref.annotation.visibility) {
case NgDirective.CHILDREN_VISIBILITY:
visibility = null;
break;
case NgDirective.DIRECT_CHILDREN_VISIBILITY:
visibility = _elementDirectChildren;
break;
}

if (ref.type == NgTextMustacheDirective) {
nodeModule.factory(NgTextMustacheDirective, (Injector injector) {
return new NgTextMustacheDirective(
Expand Down Expand Up @@ -173,9 +150,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.
Expand Down
4 changes: 1 addition & 3 deletions lib/core_dom/view_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ class _ComponentFactory implements Function {
..value(TemplateLoader, templateLoader)
..value(dom.ShadowRoot, shadowDom)
..factory(ElementProbe, (_) => probe);
shadowInjector = injector.createChild([shadowModule], name: _SHADOW);
shadowInjector = injector.createChild([shadowModule], name: SHADOW_DOM_INJECTOR_NAME);
probe = _expando[shadowDom] = new ElementProbe(
injector.get(ElementProbe), shadowDom, shadowInjector, shadowScope);
return shadowInjector;
Expand All @@ -255,8 +255,6 @@ class _AnchorAttrs extends NodeAttrs {
}
}

String _SHADOW = 'SHADOW_INJECTOR';

String _html(obj) {
if (obj is String) {
return obj;
Expand Down
12 changes: 8 additions & 4 deletions lib/directive/ng_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,26 @@ part of angular.directive;
*/
@NgDirective(
selector: 'form',
publishTypes : const <Type>[NgControl],
module: NgForm.module,
visibility: NgDirective.CHILDREN_VISIBILITY)
@NgDirective(
selector: 'fieldset',
publishTypes : const <Type>[NgControl],
module: NgForm.module,
visibility: NgDirective.CHILDREN_VISIBILITY)
@NgDirective(
selector: '.ng-form',
publishTypes : const <Type>[NgControl],
module: NgForm.module,
visibility: NgDirective.CHILDREN_VISIBILITY)
@NgDirective(
selector: '[ng-form]',
publishTypes : const <Type>[NgControl],
module: NgForm.module,
map: const { 'ng-form': '@name' },
visibility: NgDirective.CHILDREN_VISIBILITY)
class NgForm extends NgControl {
static final Module _module = new Module()
..factory(NgControl, (i) => i.get(NgForm), visibility: NgDirective.CHILDREN_VISIBILITY);

This comment has been minimized.

Copy link
@vicb

vicb Apr 2, 2014

Contributor

4 ws

static module() => _module;

This comment has been minimized.

Copy link
@vicb

vicb Apr 2, 2014

Contributor

return type


final Scope _scope;

/**
Expand Down
8 changes: 7 additions & 1 deletion lib/routing/ng_bind_route.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,20 @@ part of angular.routing;
*/
@NgDirective(
visibility: NgDirective.CHILDREN_VISIBILITY,
publishTypes: const [RouteProvider],
selector: '[ng-bind-route]',
module: NgBindRouteDirective.module,
map: const {'ng-bind-route': '@routeName'})
class NgBindRouteDirective implements RouteProvider {
Router _router;
String routeName;
Injector _injector;

static final Module _module = new Module()
..factory(RouteProvider,
(i) => i.get(NgBindRouteDirective),
visibility: NgDirective.CHILDREN_VISIBILITY);
static module() => _module;

// We inject NgRoutingHelper to force initialization of routing.
NgBindRouteDirective(this._router, this._injector, NgRoutingHelper _);

Expand Down
8 changes: 7 additions & 1 deletion lib/routing/ng_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,15 @@ part of angular.routing;
*/
@NgDirective(
selector: 'ng-view',
publishTypes: const [RouteProvider],
module: NgViewDirective.module,
visibility: NgDirective.CHILDREN_VISIBILITY)
class NgViewDirective implements NgDetachAware, RouteProvider {
static final Module _module = new Module()
..factory(RouteProvider,
(i) => i.get(RouteProvider),
visibility: NgDirective.CHILDREN_VISIBILITY);
static module() => _module;

final NgRoutingHelper locationService;
final ViewCache viewCache;
final Injector injector;
Expand Down
8 changes: 6 additions & 2 deletions test/core/core_directive_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.module);
expect(annotation.template).toEqual('template');
expect(annotation.templateUrl).toEqual('templateUrl');
expect(annotation.cssUrls).toEqual(['cssUrls']);
Expand Down Expand Up @@ -86,13 +86,17 @@ class NullParser implements Parser {
applyAuthorStyles: true,
resetStyleInheritance: true,
publishAs: 'ctrl',
publishTypes: const [String],
module: AnnotatedIoComponent.module,
visibility: NgDirective.LOCAL_VISIBILITY,
exportExpressions: const ['exportExpressions'],
map: const {
'foo': '=>foo'
})
class AnnotatedIoComponent {
static module() => new Module()..factory(String,
(i) => i.get(AnnotatedIoComponent),
visibility: NgDirective.LOCAL_VISIBILITY);

AnnotatedIoComponent(Scope scope) {
scope.rootScope.context['ioComponent'] = this;
}
Expand Down
6 changes: 5 additions & 1 deletion test/core_dom/compiler_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -727,8 +727,12 @@ class PublishTypesDirectiveSuperType {

@NgDirective(
selector: '[publish-types]',
publishTypes: const [PublishTypesDirectiveSuperType])
module: PublishTypesAttrDirective.module)
class PublishTypesAttrDirective implements PublishTypesDirectiveSuperType {
static Module _module = new Module()
..factory(PublishTypesDirectiveSuperType, (i) => i.get(PublishTypesAttrDirective));
static module() => _module;

static Injector _injector;
PublishTypesAttrDirective(Injector injector) {
_injector = injector;
Expand Down

0 comments on commit 5ec7e83

Please sign in to comment.