From 3607d62e6f735b3f86fa9850e7de945c90265840 Mon Sep 17 00:00:00 2001 From: Rado Kirov Date: Fri, 18 Jul 2014 14:00:29 -0700 Subject: [PATCH] fix(directive-injector): Fixes for Direcive Injector. Breaking change: Regular (application) injectors cannot construct DirectiveInjectors (DI). Only compilers create DI as part of view creation process. Deprecation: directive injector parent is now private. New public method on dependency injector - parentGet, which allows to get through the usual chain but skipping itself. Component Injectors now break the resolution chain (except when called directly.) TestBed does not need DI in its constructor. Internal changes: - Application Injector reference is passed through view creation and passed into new Directive Injector (instead of using parentInjector.appInjector, which is wrong when used with ng-view). - Unwind recursion from the directive injector. - Remove EventListener from View. - Replace DefaultDirectiveInjector with DirectiveInjector (with parent = null). - Component visibility handled outside the visibility enum. - Removed Shadowless and ShadowDirectiveInjector subclasses. Closes #1111 --- lib/application.dart | 2 +- lib/core_dom/common.dart | 18 -- lib/core_dom/directive_injector.dart | 184 ++++++++---------- lib/core_dom/directive_map.dart | 7 +- lib/core_dom/element_binder.dart | 27 +-- lib/core_dom/element_binder_builder.dart | 16 +- lib/core_dom/module_internal.dart | 6 +- lib/core_dom/selector.dart | 22 ++- .../shadow_dom_component_factory.dart | 17 +- .../transcluding_component_factory.dart | 9 +- lib/core_dom/view.dart | 7 +- lib/core_dom/view_factory.dart | 22 +-- lib/directive/module.dart | 1 - lib/mock/test_bed.dart | 8 +- lib/routing/ng_view.dart | 18 +- test/core/templateurl_spec.dart | 22 +-- test/core_dom/compiler_spec.dart | 96 ++++++++- test/core_dom/directive_injector_spec.dart | 2 - .../core_dom/element_binder_builder_spec.dart | 4 +- test/core_dom/mustache_spec.dart | 8 +- test/core_dom/view_spec.dart | 10 +- test/directive/ng_bind_html_spec.dart | 4 +- test/directive/ng_bind_spec.dart | 2 +- test/directive/ng_form_spec.dart | 2 +- test/directive/ng_if_spec.dart | 2 +- test/directive/ng_non_bindable_spec.dart | 2 +- test/directive/ng_repeat_spec.dart | 12 +- test/introspection_spec.dart | 4 +- test/routing/routing_spec.dart | 38 +++- 29 files changed, 339 insertions(+), 233 deletions(-) diff --git a/lib/application.dart b/lib/application.dart index 5e39c4a09..3773e4284 100644 --- a/lib/application.dart +++ b/lib/application.dart @@ -189,7 +189,7 @@ abstract class Application { DirectiveMap directiveMap = injector.getByKey(DIRECTIVE_MAP_KEY); RootScope rootScope = injector.getByKey(ROOT_SCOPE_KEY); ViewFactory viewFactory = compiler(rootElements, directiveMap); - viewFactory(rootScope, injector.get(DirectiveInjector), rootElements); + viewFactory(rootScope, null, rootElements); } catch (e, s) { exceptionHandler(e, s); } diff --git a/lib/core_dom/common.dart b/lib/core_dom/common.dart index 2db27c9bd..09fe23e5f 100644 --- a/lib/core_dom/common.dart +++ b/lib/core_dom/common.dart @@ -45,21 +45,3 @@ class DirectiveRef { } } -/** - * Creates a child injector that allows loading new directives, formatters and - * services from the provided modules. - */ -Injector forceNewDirectivesAndFormatters(Injector injector, DirectiveInjector dirInjector, - List modules) { - modules.add(new Module() - ..bind(Scope, toFactory: (Injector injector) { - var scope = injector.parent.getByKey(SCOPE_KEY); - return scope.createChild(new PrototypeMap(scope.context)); - }, inject: [INJECTOR_KEY]) - ..bind(DirectiveMap) - ..bind(FormatterMap) - ..bind(DirectiveInjector, - toFactory: () => new DefaultDirectiveInjector.newAppInjector(dirInjector, injector))); - - return new ModuleInjector(modules, injector); -} diff --git a/lib/core_dom/directive_injector.dart b/lib/core_dom/directive_injector.dart index 07959541d..12a46e383 100644 --- a/lib/core_dom/directive_injector.dart +++ b/lib/core_dom/directive_injector.dart @@ -24,13 +24,11 @@ final CONTENT_PORT_KEY = new Key(ContentPort); final TEMPLATE_LOADER_KEY = new Key(TemplateLoader); final SHADOW_ROOT_KEY = new Key(ShadowRoot); +final num MAX_DEPTH = 1 << 30; + const int VISIBILITY_LOCAL = -1; const int VISIBILITY_DIRECT_CHILD = -2; const int VISIBILITY_CHILDREN = -3; -const int VISIBILITY_COMPONENT_OFFSET = VISIBILITY_CHILDREN; -const int VISIBILITY_COMPONENT_LOCAL = VISIBILITY_LOCAL + VISIBILITY_COMPONENT_OFFSET; -const int VISIBILITY_COMPONENT_DIRECT_CHILD = VISIBILITY_DIRECT_CHILD + VISIBILITY_COMPONENT_OFFSET; -const int VISIBILITY_COMPONENT_CHILDREN = VISIBILITY_CHILDREN + VISIBILITY_COMPONENT_OFFSET; const int UNDEFINED_ID = 0; const int INJECTOR_KEY_ID = 1; @@ -52,6 +50,8 @@ const int CONTENT_PORT_KEY_ID = 16; const int EVENT_HANDLER_KEY_ID = 17; const int KEEP_ME_LAST = 18; +EventHandler eventHandler(DirectiveInjector di) => di._eventHandler; + class DirectiveInjector implements DirectiveBinder { static bool _isInit = false; static initUID() { @@ -99,9 +99,9 @@ class DirectiveInjector implements DirectiveBinder { , EVENT_HANDLER_KEY , KEEP_ME_LAST ]; - + final DirectiveInjector _parent; - final Injector appInjector; + final Injector _appInjector; final Node _node; final NodeAttrs _nodeAttrs; final Animate _animate; @@ -134,21 +134,17 @@ class DirectiveInjector implements DirectiveBinder { case VISIBILITY_LOCAL: return Visibility.LOCAL; case VISIBILITY_DIRECT_CHILD: return Visibility.DIRECT_CHILD; case VISIBILITY_CHILDREN: return Visibility.CHILDREN; - case VISIBILITY_COMPONENT_LOCAL: return Visibility.LOCAL; - case VISIBILITY_COMPONENT_DIRECT_CHILD: return Visibility.DIRECT_CHILD; - case VISIBILITY_COMPONENT_CHILDREN: return Visibility.CHILDREN; default: return null; } } static Binding _tempBinding = new Binding(); - DirectiveInjector(DirectiveInjector parent, Injector appInjector, this._node, this._nodeAttrs, + DirectiveInjector(this._parent, appInjector, this._node, this._nodeAttrs, this._eventHandler, this.scope, this._animate) - : appInjector = appInjector, - _parent = parent == null ? new DefaultDirectiveInjector(appInjector) : parent; + : _appInjector = appInjector; - DirectiveInjector._default(this._parent, this.appInjector) + DirectiveInjector._default(this._parent, this._appInjector) : _node = null, _nodeAttrs = null, _eventHandler = null, @@ -195,13 +191,15 @@ class DirectiveInjector implements DirectiveBinder { else { throw 'Maximum number of directives per element reached.'; } } + // Get a key from the directive injector chain. When it is exhausted, get from + // the current application injector chain. Object get(Type type) => getByKey(new Key(type)); Object getFromParent(Type type) => _parent.get(type); Object getByKey(Key key) { var oldTag = _TAG_GET.makeCurrent(); try { - return _getByKey(key); + return _getByKey(key, _appInjector); } on ResolvingError catch (e, s) { e.appendKey(key); rethrow; @@ -209,40 +207,56 @@ class DirectiveInjector implements DirectiveBinder { oldTag.makeCurrent(); } } - Object getFromParentByKey(Key key) => _parent.getByKey(key); - Object _getByKey(Key key) { + Object getFromParentByKey(Key key) { + if (_parent == null) { + return _appInjector.getByKey(key); + } else { + return _parent._getByKey(key, _appInjector); + } + } + + Object _getByKey(Key key, Injector appInjector) { int uid = key.uid; if (uid == null || uid == UNDEFINED_ID) return appInjector.getByKey(key); bool isDirective = uid < 0; return isDirective ? _getDirectiveByKey(key, uid, appInjector) : _getById(uid); } - Object _getDirectiveByKey(Key k, int visType, Injector i) { - do { - if (_key0 == null) break; if (identical(_key0, k)) return _obj0 == null ? _obj0 = _new(_pKeys0, _factory0) : _obj0; - if (_key1 == null) break; if (identical(_key1, k)) return _obj1 == null ? _obj1 = _new(_pKeys1, _factory1) : _obj1; - if (_key2 == null) break; if (identical(_key2, k)) return _obj2 == null ? _obj2 = _new(_pKeys2, _factory2) : _obj2; - if (_key3 == null) break; if (identical(_key3, k)) return _obj3 == null ? _obj3 = _new(_pKeys3, _factory3) : _obj3; - if (_key4 == null) break; if (identical(_key4, k)) return _obj4 == null ? _obj4 = _new(_pKeys4, _factory4) : _obj4; - if (_key5 == null) break; if (identical(_key5, k)) return _obj5 == null ? _obj5 = _new(_pKeys5, _factory5) : _obj5; - if (_key6 == null) break; if (identical(_key6, k)) return _obj6 == null ? _obj6 = _new(_pKeys6, _factory6) : _obj6; - if (_key7 == null) break; if (identical(_key7, k)) return _obj7 == null ? _obj7 = _new(_pKeys7, _factory7) : _obj7; - if (_key8 == null) break; if (identical(_key8, k)) return _obj8 == null ? _obj8 = _new(_pKeys8, _factory8) : _obj8; - if (_key9 == null) break; if (identical(_key9, k)) return _obj9 == null ? _obj9 = _new(_pKeys9, _factory9) : _obj9; - } while (false); - switch (visType) { - case VISIBILITY_LOCAL: return appInjector.getByKey(k); - case VISIBILITY_DIRECT_CHILD: return _parent._getDirectiveByKey(k, VISIBILITY_LOCAL, i); - case VISIBILITY_CHILDREN: return _parent._getDirectiveByKey(k, VISIBILITY_CHILDREN, i); - // SHADOW - case VISIBILITY_COMPONENT_LOCAL: return _parent._getDirectiveByKey(k, VISIBILITY_LOCAL, i); - case VISIBILITY_COMPONENT_DIRECT_CHILD: return _parent._getDirectiveByKey(k, VISIBILITY_DIRECT_CHILD, i); - case VISIBILITY_COMPONENT_CHILDREN: return _parent._getDirectiveByKey(k, VISIBILITY_CHILDREN, i); + num _getDepth(int visType) { + switch(visType) { + case VISIBILITY_LOCAL: return 0; + case VISIBILITY_DIRECT_CHILD: return 1; + case VISIBILITY_CHILDREN: return MAX_DEPTH; default: throw null; } } + _getDirectiveByKey(Key k, int visType, Injector appInjector) { + num depth = _getDepth(visType); + // ci stands for currentInjector, abbreviated for readability. + var ci = this; + while (ci != null && depth >= 0) { + do { + if (ci._key0 == null) break; if (identical(ci._key0, k)) return ci._obj0 == null ? ci._obj0 = ci._new(ci._pKeys0, ci._factory0) : ci._obj0; + if (ci._key1 == null) break; if (identical(ci._key1, k)) return ci._obj1 == null ? ci._obj1 = ci._new(ci._pKeys1, ci._factory1) : ci._obj1; + if (ci._key2 == null) break; if (identical(ci._key2, k)) return ci._obj2 == null ? ci._obj2 = ci._new(ci._pKeys2, ci._factory2) : ci._obj2; + if (ci._key3 == null) break; if (identical(ci._key3, k)) return ci._obj3 == null ? ci._obj3 = ci._new(ci._pKeys3, ci._factory3) : ci._obj3; + if (ci._key4 == null) break; if (identical(ci._key4, k)) return ci._obj4 == null ? ci._obj4 = ci._new(ci._pKeys4, ci._factory4) : ci._obj4; + if (ci._key5 == null) break; if (identical(ci._key5, k)) return ci._obj5 == null ? ci._obj5 = ci._new(ci._pKeys5, ci._factory5) : ci._obj5; + if (ci._key6 == null) break; if (identical(ci._key6, k)) return ci._obj6 == null ? ci._obj6 = ci._new(ci._pKeys6, ci._factory6) : ci._obj6; + if (ci._key7 == null) break; if (identical(ci._key7, k)) return ci._obj7 == null ? ci._obj7 = ci._new(ci._pKeys7, ci._factory7) : ci._obj7; + if (ci._key8 == null) break; if (identical(ci._key8, k)) return ci._obj8 == null ? ci._obj8 = ci._new(ci._pKeys8, ci._factory8) : ci._obj8; + if (ci._key9 == null) break; if (identical(ci._key9, k)) return ci._obj9 == null ? ci._obj9 = ci._new(ci._pKeys9, ci._factory9) : ci._obj9; + } while (false); + // Component Injectors fall-through only if directly called. + if ((ci is ComponentDirectiveInjector) && !identical(ci, this)) break; + ci = ci._parent; + depth--; + } + return appInjector.getByKey(k); + } + List get directives { var directives = []; if (_obj0 != null) directives.add(_obj0); @@ -260,7 +274,7 @@ class DirectiveInjector implements DirectiveBinder { Object _getById(int keyId) { switch(keyId) { - case INJECTOR_KEY_ID: return appInjector; + case INJECTOR_KEY_ID: return _appInjector; case DIRECTIVE_INJECTOR_KEY_ID: return this; case NODE_KEY_ID: return _node; case ELEMENT_KEY_ID: return _node; @@ -270,7 +284,13 @@ class DirectiveInjector implements DirectiveBinder { case ELEMENT_PROBE_KEY_ID: return elementProbe; case NG_ELEMENT_KEY_ID: return ngElement; case EVENT_HANDLER_KEY_ID: return _eventHandler; - case CONTENT_PORT_KEY_ID: return _parent._getById(keyId); + case CONTENT_PORT_KEY_ID: + var currentInjector = _parent; + while (currentInjector != null) { + if (currentInjector is ComponentDirectiveInjector) return currentInjector._contentPort; + currentInjector = currentInjector._parent; + } + return null; default: new NoProviderError(_KEYS[keyId]); } } @@ -279,29 +299,30 @@ class DirectiveInjector implements DirectiveBinder { var oldTag = _TAG_GET.makeCurrent(); int size = paramKeys.length; var obj; + var appInjector = this._appInjector; if (size > 15) { var params = new List(paramKeys.length); for(var i = 0; i < paramKeys.length; i++) { - params[i] = _getByKey(paramKeys[i]); + params[i] = _getByKey(paramKeys[i], appInjector); } _TAG_INSTANTIATE.makeCurrent(); obj = Function.apply(fn, params); } else { - var a01 = size >= 01 ? _getByKey(paramKeys[00]) : null; - var a02 = size >= 02 ? _getByKey(paramKeys[01]) : null; - var a03 = size >= 03 ? _getByKey(paramKeys[02]) : null; - var a04 = size >= 04 ? _getByKey(paramKeys[03]) : null; - var a05 = size >= 05 ? _getByKey(paramKeys[04]) : null; - var a06 = size >= 06 ? _getByKey(paramKeys[05]) : null; - var a07 = size >= 07 ? _getByKey(paramKeys[06]) : null; - var a08 = size >= 08 ? _getByKey(paramKeys[07]) : null; - var a09 = size >= 09 ? _getByKey(paramKeys[08]) : null; - var a10 = size >= 10 ? _getByKey(paramKeys[09]) : null; - var a11 = size >= 11 ? _getByKey(paramKeys[10]) : null; - var a12 = size >= 12 ? _getByKey(paramKeys[11]) : null; - var a13 = size >= 13 ? _getByKey(paramKeys[12]) : null; - var a14 = size >= 14 ? _getByKey(paramKeys[13]) : null; - var a15 = size >= 15 ? _getByKey(paramKeys[14]) : null; + var a01 = size >= 01 ? _getByKey(paramKeys[00], appInjector) : null; + var a02 = size >= 02 ? _getByKey(paramKeys[01], appInjector) : null; + var a03 = size >= 03 ? _getByKey(paramKeys[02], appInjector) : null; + var a04 = size >= 04 ? _getByKey(paramKeys[03], appInjector) : null; + var a05 = size >= 05 ? _getByKey(paramKeys[04], appInjector) : null; + var a06 = size >= 06 ? _getByKey(paramKeys[05], appInjector) : null; + var a07 = size >= 07 ? _getByKey(paramKeys[06], appInjector) : null; + var a08 = size >= 08 ? _getByKey(paramKeys[07], appInjector) : null; + var a09 = size >= 09 ? _getByKey(paramKeys[08], appInjector) : null; + var a10 = size >= 10 ? _getByKey(paramKeys[09], appInjector) : null; + var a11 = size >= 11 ? _getByKey(paramKeys[10], appInjector) : null; + var a12 = size >= 12 ? _getByKey(paramKeys[11], appInjector) : null; + var a13 = size >= 13 ? _getByKey(paramKeys[12], appInjector) : null; + var a14 = size >= 14 ? _getByKey(paramKeys[13], appInjector) : null; + var a15 = size >= 15 ? _getByKey(paramKeys[14], appInjector) : null; _TAG_INSTANTIATE.makeCurrent(); switch(size) { case 00: obj = fn(); break; @@ -367,14 +388,15 @@ class TemplateDirectiveInjector extends DirectiveInjector { } -abstract class ComponentDirectiveInjector extends DirectiveInjector { +class ComponentDirectiveInjector extends DirectiveInjector { final TemplateLoader _templateLoader; final ShadowRoot _shadowRoot; + final ContentPort _contentPort; ComponentDirectiveInjector(DirectiveInjector parent, Injector appInjector, EventHandler eventHandler, Scope scope, - this._templateLoader, this._shadowRoot) + this._templateLoader, this._shadowRoot, this._contentPort) : super(parent, appInjector, parent._node, parent._nodeAttrs, eventHandler, scope, parent._animate); @@ -386,34 +408,6 @@ abstract class ComponentDirectiveInjector extends DirectiveInjector { } } - _getDirectiveByKey(Key k, int visType, Injector i) => - super._getDirectiveByKey(k, visType + VISIBILITY_COMPONENT_OFFSET, i); -} - -class ShadowlessComponentDirectiveInjector extends ComponentDirectiveInjector { - final ContentPort _contentPort; - - ShadowlessComponentDirectiveInjector(DirectiveInjector parent, Injector appInjector, - EventHandler eventHandler, Scope scope, - templateLoader, shadowRoot, this._contentPort) - : super(parent, appInjector, eventHandler, scope, templateLoader, shadowRoot); - - Object _getById(int keyId) { - switch(keyId) { - case CONTENT_PORT_KEY_ID: return _contentPort; - default: return super._getById(keyId); - } - } -} - -class ShadowDomComponentDirectiveInjector extends ComponentDirectiveInjector { - ShadowDomComponentDirectiveInjector(DirectiveInjector parent, Injector appInjector, - Scope scope, templateLoader, shadowRoot) - : super(parent, appInjector, new ShadowRootEventHandler(shadowRoot, - parent.getByKey(EXPANDO_KEY), - parent.getByKey(EXCEPTION_HANDLER_KEY)), - scope, templateLoader, shadowRoot); - ElementProbe get elementProbe { if (_elementProbe == null) { ElementProbe parentProbe = _parent == null ? null : _parent.elementProbe; @@ -421,21 +415,9 @@ class ShadowDomComponentDirectiveInjector extends ComponentDirectiveInjector { } return _elementProbe; } -} -@Injectable() -class DefaultDirectiveInjector extends DirectiveInjector { - DefaultDirectiveInjector(Injector appInjector): super._default(null, appInjector); - DefaultDirectiveInjector.newAppInjector(DirectiveInjector parent, Injector appInjector) - : super._default(parent, appInjector); - - Object getByKey(Key key) => appInjector.getByKey(key); - _getDirectiveByKey(Key key, int visType, Injector i) => - _parent == null ? i.getByKey(key) : _parent._getDirectiveByKey(key, visType, i); - _getById(int keyId) { - switch (keyId) { - case CONTENT_PORT_KEY_ID: return null; - default: throw new NoProviderError(DirectiveInjector._KEYS[keyId]); - } - } + // Add 1 to visibility to allow to skip over current component injector. + // For example, a local directive is visible from its component injector children. + num _getDepth(int visType) => super._getDepth(visType) + 1; } + diff --git a/lib/core_dom/directive_map.dart b/lib/core_dom/directive_map.dart index 576587763..c09dd1c9f 100644 --- a/lib/core_dom/directive_map.dart +++ b/lib/core_dom/directive_map.dart @@ -13,12 +13,13 @@ class DirectiveMap { final DirectiveSelectorFactory _directiveSelectorFactory; FormatterMap _formatters; DirectiveSelector _selector; + Injector _injector; - DirectiveMap(Injector injector, + DirectiveMap(Injector this._injector, this._formatters, MetadataExtractor metadataExtractor, this._directiveSelectorFactory) { - (injector as ModuleInjector).types.forEach((type) { + (_injector as ModuleInjector).types.forEach((type) { metadataExtractor(type) .where((annotation) => annotation is Directive) .forEach((Directive dir) { @@ -29,7 +30,7 @@ class DirectiveMap { DirectiveSelector get selector { if (_selector != null) return _selector; - return _selector = _directiveSelectorFactory.selector(this, _formatters); + return _selector = _directiveSelectorFactory.selector(this, _injector, _formatters); } List operator[](String key) { diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart index da70b4566..fc00874e2 100644 --- a/lib/core_dom/element_binder.dart +++ b/lib/core_dom/element_binder.dart @@ -15,10 +15,10 @@ class TemplateElementBinder extends ElementBinder { return _directiveCache = [template]; } - TemplateElementBinder(perf, expando, parser, config, + TemplateElementBinder(perf, expando, parser, config, appInjector, this.template, this.templateBinder, onEvents, bindAttrs, childMode) - : super(perf, expando, parser, config, + : super(perf, expando, parser, config, appInjector, null, null, onEvents, bindAttrs, childMode); String toString() => "[TemplateElementBinder template:$template]"; @@ -34,6 +34,8 @@ class ElementBinder { final Expando _expando; final Parser _parser; final CompilerConfig _config; + final Injector _appInjector; + Animate _animate; final Map onEvents; final Map bindAttrs; @@ -47,8 +49,10 @@ class ElementBinder { final String childMode; ElementBinder(this._perf, this._expando, this._parser, this._config, - this.componentData, this.decorators, - this.onEvents, this.bindAttrs, this.childMode); + this._appInjector, this.componentData, this.decorators, + this.onEvents, this.bindAttrs, this.childMode) { + _animate = _appInjector.getByKey(ANIMATE_KEY); + } final bool hasTemplate = false; @@ -252,20 +256,21 @@ class ElementBinder { DirectiveInjector bind(View view, Scope scope, DirectiveInjector parentInjector, - dom.Node node, EventHandler eventHandler, Animate animate) { + dom.Node node) { var nodeAttrs = node is dom.Element ? new NodeAttrs(node) : null; var directiveRefs = _usableDirectiveRefs; if (!hasDirectivesOrEvents) return parentInjector; DirectiveInjector nodeInjector; + var parentEventHandler = parentInjector == null ? + _appInjector.getByKey(EVENT_HANDLER_KEY) : + eventHandler(parentInjector); if (this is TemplateElementBinder) { - nodeInjector = new TemplateDirectiveInjector(parentInjector, parentInjector.appInjector, - node, nodeAttrs, eventHandler, scope, animate, - (this as TemplateElementBinder).templateViewFactory); + nodeInjector = new TemplateDirectiveInjector(parentInjector, _appInjector, + node, nodeAttrs, parentEventHandler, scope, _animate, (this as TemplateElementBinder).templateViewFactory); } else { - nodeInjector = new DirectiveInjector(parentInjector, parentInjector.appInjector, - node, nodeAttrs, eventHandler, scope, animate); + nodeInjector = new DirectiveInjector(parentInjector, _appInjector, node, nodeAttrs, parentEventHandler, scope, _animate); } for(var i = 0; i < directiveRefs.length; i++) { @@ -315,7 +320,7 @@ class ElementBinder { if (onEvents.isNotEmpty) { onEvents.forEach((event, value) { - view.registerEvent(EventHandler.attrNameToEventName(event)); + parentEventHandler.register(EventHandler.attrNameToEventName(event)); }); } return nodeInjector; diff --git a/lib/core_dom/element_binder_builder.dart b/lib/core_dom/element_binder_builder.dart index dfa070d71..9983a1365 100644 --- a/lib/core_dom/element_binder_builder.dart +++ b/lib/core_dom/element_binder_builder.dart @@ -15,16 +15,17 @@ class ElementBinderFactory { this.astParser, this.componentFactory, this.shadowDomComponentFactory, this.transcludingComponentFactory); // TODO: Optimize this to re-use a builder. - ElementBinderBuilder builder(FormatterMap formatters, DirectiveMap directives) => - new ElementBinderBuilder(this,formatters, directives); + ElementBinderBuilder builder(FormatterMap formatters, DirectiveMap directives, Injector injector) => + new ElementBinderBuilder(this, formatters, directives, injector); ElementBinder binder(ElementBinderBuilder b) => - new ElementBinder(_perf, _expando, _parser, _config, + new ElementBinder(_perf, _expando, _parser, _config, b._injector, b.componentData, b.decorators, b.onEvents, b.bindAttrs, b.childMode); - TemplateElementBinder templateBinder(ElementBinderBuilder b, ElementBinder transclude) => - new TemplateElementBinder(_perf, _expando, _parser, _config, + TemplateElementBinder templateBinder( + ElementBinderBuilder b, ElementBinder transclude) => + new TemplateElementBinder(_perf, _expando, _parser, _config, b._injector, b.template, transclude, b.onEvents, b.bindAttrs, b.childMode); } @@ -45,13 +46,14 @@ class ElementBinderBuilder { final bindAttrs = new HashMap(); final decorators = []; + final Injector _injector; DirectiveRef template; BoundComponentData componentData; // Can be either COMPILE_CHILDREN or IGNORE_CHILDREN String childMode = Directive.COMPILE_CHILDREN; - ElementBinderBuilder(this._factory, this._formatters, this._directives); + ElementBinderBuilder(this._factory, this._formatters, this._directives, this._injector); /** * Adds [DirectiveRef]s to this [ElementBinderBuilder]. @@ -79,7 +81,7 @@ class ElementBinderBuilder { factory = _factory.componentFactory; } - componentData = new BoundComponentData(ref, () => factory.bind(ref, _directives)); + componentData = new BoundComponentData(ref, () => factory.bind(ref, _directives, _injector)); } else { decorators.add(ref); } diff --git a/lib/core_dom/module_internal.dart b/lib/core_dom/module_internal.dart index 72f7e06d0..a6138089a 100644 --- a/lib/core_dom/module_internal.dart +++ b/lib/core_dom/module_internal.dart @@ -60,8 +60,7 @@ class CoreDomModule extends Module { bind(TemplateCache, toFactory: (CacheRegister register) { var templateCache = new TemplateCache(); register.registerCache("TemplateCache", templateCache); - return templateCache; - }, inject: [CACHE_REGISTER_KEY]); + return templateCache; }, inject: [CACHE_REGISTER_KEY]); bind(dom.NodeTreeSanitizer, toImplementation: NullTreeSanitizer); bind(TextMustache); @@ -95,5 +94,8 @@ class CoreDomModule extends Module { bind(ElementBinderFactory); bind(NgElement); bind(EventHandler); + // TODO(rkirov): remove this once clients have stopped relying on it. + bind(DirectiveInjector, toValue: null); + } } diff --git a/lib/core_dom/selector.dart b/lib/core_dom/selector.dart index 1bb1b9977..32ab75725 100644 --- a/lib/core_dom/selector.dart +++ b/lib/core_dom/selector.dart @@ -29,12 +29,14 @@ class DirectiveSelector { Interpolate _interpolate; FormatterMap _formatters; ASTParser _astParser; + final Injector _injector; var elementSelector = new _ElementSelector(''); var attrSelector = <_ContainsSelector>[]; var textSelector = <_ContainsSelector>[]; /// Parses all the [_directives] so they can be retrieved via [matchElement] - DirectiveSelector(this._directives, this._formatters, this._binderFactory, this._interpolate, this._astParser) { + DirectiveSelector(this._directives, this._formatters, this._binderFactory, this._interpolate, + this._astParser, this._injector) { _directives.forEach((Directive annotation, Type type) { var match; var selector = annotation.selector; @@ -62,7 +64,7 @@ class DirectiveSelector { ElementBinder matchElement(dom.Node node) { assert(node is dom.Element); - ElementBinderBuilder builder = _binderFactory.builder(_formatters, _directives); + ElementBinderBuilder builder = _binderFactory.builder(_formatters, _directives, _injector); List<_ElementSelector> partialSelection; final classes = new Set(); final attrs = new HashMap(); @@ -133,7 +135,7 @@ class DirectiveSelector { } ElementBinder matchText(dom.Node node) { - ElementBinderBuilder builder = _binderFactory.builder(_formatters, _directives); + ElementBinderBuilder builder = _binderFactory.builder(_formatters, _directives, _injector); var value = node.nodeValue; for (var k = 0; k < textSelector.length; k++) { @@ -152,7 +154,7 @@ class DirectiveSelector { return builder.binder; } - ElementBinder matchComment(dom.Node node) => _binderFactory.builder(null, null).binder; + ElementBinder matchComment(dom.Node node) => _binderFactory.builder(null, null, _injector).binder; } /** @@ -166,19 +168,23 @@ class DirectiveSelectorFactory { // TODO(deboer): Remove once the FormatterMap is a required 'selector' parameter. FormatterMap _defaultFormatterMap; + // TODO(rkirov): Remove once the injector is a required 'selector' parameter. + Injector _defaultInjector; + DirectiveSelectorFactory(this._binderFactory, this._interpolate, - this._astParser, this._defaultFormatterMap); + this._astParser, this._defaultFormatterMap, this._defaultInjector); /** * Create a new [DirectiveSelector] given a [DirectiveMap] and [FormatterMap] * - * NOTE: [formatters] will become required very soon. New code must pass + * NOTE: [injector and formatters] will become required very soon. New code must pass * both parameters. */ - DirectiveSelector selector(DirectiveMap directives, [FormatterMap formatters]) => + DirectiveSelector selector(DirectiveMap directives, [Injector injector, FormatterMap formatters]) => new DirectiveSelector(directives, formatters != null ? formatters : _defaultFormatterMap, - _binderFactory, _interpolate, _astParser); + _binderFactory, _interpolate, _astParser, + injector != null ? injector : _defaultInjector); } class _Directive { diff --git a/lib/core_dom/shadow_dom_component_factory.dart b/lib/core_dom/shadow_dom_component_factory.dart index d6fff8dcd..221701575 100644 --- a/lib/core_dom/shadow_dom_component_factory.dart +++ b/lib/core_dom/shadow_dom_component_factory.dart @@ -1,7 +1,7 @@ part of angular.core.dom_internal; abstract class ComponentFactory { - BoundComponentFactory bind(DirectiveRef ref, directives); + BoundComponentFactory bind(DirectiveRef ref, directives, Injector injector); } /** @@ -52,8 +52,8 @@ class ShadowDomComponentFactory implements ComponentFactory { cacheRegister.registerCache("ShadowDomComponentFactoryStyles", styleElementCache); } - bind(DirectiveRef ref, directives) => - new BoundShadowDomComponentFactory(this, ref, directives); + bind(DirectiveRef ref, directives, injector) => + new BoundShadowDomComponentFactory(this, ref, directives, injector); } class BoundShadowDomComponentFactory implements BoundComponentFactory { @@ -61,6 +61,8 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory { final ShadowDomComponentFactory _componentFactory; final DirectiveRef _ref; final DirectiveMap _directives; + final Injector _injector; + final DirectiveInjector _directive_injector; Component get _component => _ref.annotation as Component; @@ -68,7 +70,7 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory { async.Future> _styleElementsFuture; async.Future _viewFuture; - BoundShadowDomComponentFactory(this._componentFactory, this._ref, this._directives) { + BoundShadowDomComponentFactory(this._componentFactory, this._ref, this._directives, this._injector) { _tag = _component.selector.toLowerCase(); _styleElementsFuture = async.Future.wait(_component.cssUrls.map(_styleFuture)); @@ -123,7 +125,7 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory { EVENT_HANDLER_KEY]; Function call(dom.Element element) { return (DirectiveInjector injector, Scope scope, NgBaseCss baseCss, - EventHandler eventHandler) { + EventHandler _) { var s = traceEnter(View_createComponent); try { var shadowDom = element.createShadowRoot() @@ -161,7 +163,10 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory { })); var probe; - shadowInjector = new ShadowDomComponentDirectiveInjector(injector, injector.appInjector, shadowScope, templateLoader, shadowDom); + var eventHandler = new ShadowRootEventHandler( + shadowDom, injector.getByKey(EXPANDO_KEY), injector.getByKey(EXCEPTION_HANDLER_KEY)); + shadowInjector = new ComponentDirectiveInjector(injector, _injector, eventHandler, shadowScope, + templateLoader, shadowDom, null); shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility); if (_componentFactory.config.elementProbeEnabled) { diff --git a/lib/core_dom/transcluding_component_factory.dart b/lib/core_dom/transcluding_component_factory.dart index 5cc1c652c..a3c2b4f60 100644 --- a/lib/core_dom/transcluding_component_factory.dart +++ b/lib/core_dom/transcluding_component_factory.dart @@ -77,19 +77,20 @@ class TranscludingComponentFactory implements ComponentFactory { TranscludingComponentFactory(this.expando, this.viewCache, this.config); - bind(DirectiveRef ref, directives) => - new BoundTranscludingComponentFactory(this, ref, directives); + bind(DirectiveRef ref, directives, injector) => + new BoundTranscludingComponentFactory(this, ref, directives, injector); } class BoundTranscludingComponentFactory implements BoundComponentFactory { final TranscludingComponentFactory _f; final DirectiveRef _ref; final DirectiveMap _directives; + final Injector _injector; Component get _component => _ref.annotation as Component; async.Future _viewFuture; - BoundTranscludingComponentFactory(this._f, this._ref, this._directives) { + BoundTranscludingComponentFactory(this._f, this._ref, this._directives, this._injector) { _viewFuture = BoundComponentFactory._viewFuture( _component, _f.viewCache, @@ -142,7 +143,7 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory { Scope shadowScope = scope.createChild(new HashMap()); - childInjector = new ShadowlessComponentDirectiveInjector(injector, injector.appInjector, + childInjector = new ComponentDirectiveInjector(injector, this._injector, eventHandler, shadowScope, templateLoader, new ShadowlessShadowRoot(element), contentPort); childInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility); diff --git a/lib/core_dom/view.dart b/lib/core_dom/view.dart index 1195fbe8c..c034ef458 100644 --- a/lib/core_dom/view.dart +++ b/lib/core_dom/view.dart @@ -14,13 +14,8 @@ part of angular.core.dom_internal; class View { final Scope scope; final List nodes; - final EventHandler eventHandler; - View(this.nodes, this.scope, this.eventHandler); - - void registerEvent(String eventName) { - eventHandler.register(eventName); - } + View(this.nodes, this.scope); } /** diff --git a/lib/core_dom/view_factory.dart b/lib/core_dom/view_factory.dart index 816e9822a..4c9952bcc 100644 --- a/lib/core_dom/view_factory.dart +++ b/lib/core_dom/view_factory.dart @@ -56,10 +56,8 @@ class ViewFactory implements Function { if (nodes == null) { nodes = cloneElements(templateNodes); } - Animate animate = directiveInjector.getByKey(ANIMATE_KEY); - EventHandler eventHandler = directiveInjector.getByKey(EVENT_HANDLER_KEY); - var view = new View(nodes, scope, eventHandler); - _link(view, scope, nodes, eventHandler, animate, directiveInjector); + var view = new View(nodes, scope); + _link(view, scope, nodes, directiveInjector); traceLeave(s); return view; @@ -67,8 +65,7 @@ class ViewFactory implements Function { void _bindTagged(TaggedElementBinder tagged, int elementBinderIndex, DirectiveInjector rootInjector, - List elementInjectors, View view, boundNode, Scope scope, - EventHandler eventHandler, Animate animate) { + List elementInjectors, View view, boundNode, Scope scope) { var binder = tagged.binder; DirectiveInjector parentInjector = tagged.parentBinderOffset == -1 ? rootInjector : elementInjectors[tagged.parentBinderOffset]; @@ -81,7 +78,7 @@ class ViewFactory implements Function { if (parentInjector != rootInjector && parentInjector.scope != null) { scope = parentInjector.scope; } - elementInjector = binder.bind(view, scope, parentInjector, boundNode, eventHandler, animate); + elementInjector = binder.bind(view, scope, parentInjector, boundNode); } // TODO(misko): Remove this after we remove controllers. No controllers -> 1to1 Scope:View. if (elementInjector != rootInjector && elementInjector.scope != null) { @@ -93,13 +90,12 @@ class ViewFactory implements Function { for (var k = 0; k < tagged.textBinders.length; k++) { TaggedTextBinder taggedText = tagged.textBinders[k]; var childNode = boundNode.childNodes[taggedText.offsetIndex]; - taggedText.binder.bind(view, scope, elementInjector, childNode, eventHandler, animate); + taggedText.binder.bind(view, scope, elementInjector, childNode); } } } - View _link(View view, Scope scope, List nodeList, EventHandler eventHandler, - Animate animate, DirectiveInjector rootInjector) { + View _link(View view, Scope scope, List nodeList, DirectiveInjector rootInjector) { var elementInjectors = new List(elementBinders.length); var directiveDefsByName = {}; @@ -121,7 +117,7 @@ class ViewFactory implements Function { if (linkingInfo.containsNgBinding) { var tagged = elementBinders[elementBinderIndex]; _bindTagged(tagged, elementBinderIndex, rootInjector, - elementInjectors, view, node, scope, eventHandler, animate); + elementInjectors, view, node, scope); elementBinderIndex++; } @@ -130,7 +126,7 @@ class ViewFactory implements Function { for (int j = 0; j < elts.length; j++, elementBinderIndex++) { TaggedElementBinder tagged = elementBinders[elementBinderIndex]; _bindTagged(tagged, elementBinderIndex, rootInjector, elementInjectors, - view, elts[j], scope, eventHandler, animate); + view, elts[j], scope); } } } else { @@ -138,7 +134,7 @@ class ViewFactory implements Function { assert(tagged.binder != null || tagged.isTopLevel); if (tagged.binder != null) { _bindTagged(tagged, elementBinderIndex, rootInjector, - elementInjectors, view, node, scope, eventHandler, animate); + elementInjectors, view, node, scope); } elementBinderIndex++; } diff --git a/lib/directive/module.dart b/lib/directive/module.dart index 29b040ced..f0f9a67d4 100644 --- a/lib/directive/module.dart +++ b/lib/directive/module.dart @@ -64,7 +64,6 @@ part 'ng_model_options.dart'; */ class DirectiveModule extends Module { DirectiveModule() { - bind(DirectiveInjector, toImplementation: DefaultDirectiveInjector); bind(AHref, toValue: null); bind(NgBaseCss); // The root injector should have an empty NgBaseCss diff --git a/lib/mock/test_bed.dart b/lib/mock/test_bed.dart index e0cf5064d..616bd235b 100644 --- a/lib/mock/test_bed.dart +++ b/lib/mock/test_bed.dart @@ -8,7 +8,6 @@ part of angular.mock; */ class TestBed { final Injector injector; - final DirectiveInjector directiveInjector; final Scope rootScope; final Compiler compiler; final Parser _parser; @@ -18,11 +17,10 @@ class TestBed { List rootElements; View rootView; - TestBed(this.injector, this.directiveInjector, this.rootScope, this.compiler, this._parser, this.expando); + TestBed(this.injector, this.rootScope, this.compiler, this._parser, this.expando); TestBed.fromInjector(Injector i) : - this(i, i.get(DirectiveInjector), i.get(RootScope), i.get(Compiler), - i.get(Parser), i.get(Expando)); + this(i, i.get(RootScope), i.get(Compiler), i.get(Parser), i.get(Expando)); /** @@ -54,7 +52,7 @@ class TestBed { if (directives == null) { directives = injector.getByKey(DIRECTIVE_MAP_KEY); } - rootView = compiler(rootElements, directives)(scope, injector.get(DirectiveInjector), rootElements); + rootView = compiler(rootElements, directives)(scope, null, rootElements); return rootElement; } diff --git a/lib/routing/ng_view.dart b/lib/routing/ng_view.dart index e2b43f513..5ebb4c4d2 100644 --- a/lib/routing/ng_view.dart +++ b/lib/routing/ng_view.dart @@ -109,11 +109,9 @@ class NgView implements DetachAware, RouteProvider { }); Injector viewInjector = _appInjector; - DirectiveInjector directiveInjector = _dirInjector; if (modules != null) { - viewInjector = forceNewDirectivesAndFormatters(_appInjector, _dirInjector, modules); - directiveInjector = viewInjector.get(DirectiveInjector); + viewInjector = createChildInjectorWithReload(_appInjector, modules); } var newDirectives = viewInjector.getByKey(DIRECTIVE_MAP_KEY); @@ -123,7 +121,7 @@ class NgView implements DetachAware, RouteProvider { viewFuture.then((ViewFactory viewFactory) { _cleanUp(); _childScope = _scope.createChild(new PrototypeMap(_scope.context)); - _view = viewFactory(_childScope, directiveInjector); + _view = viewFactory(_childScope, _dirInjector); _view.nodes.forEach((elm) => _element.append(elm)); }); } @@ -150,6 +148,18 @@ class NgView implements DetachAware, RouteProvider { } return res; } + /** + * Creates a child injector that allows loading new directives, formatters and + * services from the provided modules. + */ + static Injector createChildInjectorWithReload(Injector injector, List modules) { + var modulesToAdd = new List.from(modules); + modulesToAdd.add(new Module() + ..bind(DirectiveMap) + ..bind(FormatterMap)); + + return new ModuleInjector(modulesToAdd, injector); + } } diff --git a/test/core/templateurl_spec.dart b/test/core/templateurl_spec.dart index 6f28f62d3..450d34e0d 100644 --- a/test/core/templateurl_spec.dart +++ b/test/core/templateurl_spec.dart @@ -64,7 +64,7 @@ void main() { var element = e('
ignore
'); zone.run(() { - compile([element], directives)(rootScope, injector.get(DirectiveInjector), [element]); + compile([element], directives)(rootScope, null, [element]); }); backend.flush(); @@ -94,7 +94,7 @@ void main() { backend.expectGET('simple.html').respond(200, '
Simple!
'); var element = es('
ignore
'); - compile(element, directives)(rootScope, injector.get(DirectiveInjector), element); + compile(element, directives)(rootScope, null, element); microLeap(); backend.flush(); @@ -116,7 +116,7 @@ void main() { 'ignore' 'ignore' '
'); - compile(element, directives)(rootScope, injector.get(DirectiveInjector), element); + compile(element, directives)(rootScope, null, element); microLeap(); backend.flush(); @@ -137,7 +137,7 @@ void main() { ..expectGET('simple.html').respond(200, '
Simple!
'); var element = e('
ignore
'); - compile([element], directives)(rootScope, injector.get(DirectiveInjector), [element]); + compile([element], directives)(rootScope, null, [element]); microLeap(); backend.flush(); @@ -157,7 +157,7 @@ void main() { MockHttpBackend backend, DirectiveMap directives) { var element = es('
ignore
'); backend.expectGET('simple.css').respond(200, '.hello{}'); - compile(element, directives)(rootScope, injector.get(DirectiveInjector), element); + compile(element, directives)(rootScope, null, element); microLeap(); backend.flush(); @@ -170,7 +170,7 @@ void main() { MockHttpBackend backend, DirectiveMap directives) { var element = es('
ignore
'); backend.expectGET('simple.css').respond(500, 'some error'); - compile(element, directives)(rootScope, injector.get(DirectiveInjector), element); + compile(element, directives)(rootScope, null, element); microLeap(); backend.flush(); @@ -187,7 +187,7 @@ void main() { MockHttpBackend backend, DirectiveMap directives) { var element = es('
ignore
'); backend.expectGET('simple.css').respond(200, '.hello{}'); - compile(element, directives)(rootScope, injector.get(DirectiveInjector), element); + compile(element, directives)(rootScope, null, element); microLeap(); backend.flush(); @@ -203,7 +203,7 @@ void main() { ..expectGET('simple.html').respond(200, '
Simple!
'); var element = es('ignore'); - compile(element, directives)(rootScope, injector.get(DirectiveInjector), element); + compile(element, directives)(rootScope, null, element); microLeap(); backend.flush(); @@ -228,7 +228,7 @@ void main() { ..expectGET('simple.html').respond(200, '
Simple!
'); var element = e('
ignore
'); - compile([element], directives)(rootScope, injector.get(DirectiveInjector), [element]); + compile([element], directives)(rootScope, null, [element]); microLeap(); backend.flush(); @@ -259,7 +259,7 @@ void main() { ..expectGET('simple.html').respond(200, '
Simple!
'); var element = e('
ignore
'); - compile([element], directives)(rootScope, injector.get(DirectiveInjector), [element]); + compile([element], directives)(rootScope, null, [element]); microLeap(); backend.flush(); @@ -270,7 +270,7 @@ void main() { ); var element2 = e('
ignore
'); - compile([element2], directives)(rootScope, injector.get(DirectiveInjector), [element2]); + compile([element2], directives)(rootScope, null, [element2]); microLeap(); diff --git a/test/core_dom/compiler_spec.dart b/test/core_dom/compiler_spec.dart index 4aa1c4481..d15bf7afc 100644 --- a/test/core_dom/compiler_spec.dart +++ b/test/core_dom/compiler_spec.dart @@ -79,7 +79,10 @@ void main() { ..bind(MyScopeModifyingController) ..bind(SameNameDecorator) ..bind(SameNameTransclude) - ..bind(ScopeAwareComponent); + ..bind(ScopeAwareComponent) + ..bind(Parent, toValue: null) + ..bind(Child) + ..bind(ChildTemplateComponent); }); beforeEach((TestBed tb) => _ = tb); @@ -301,7 +304,9 @@ void main() { ..bind(ExprAttrComponent) ..bind(LogElementComponent) ..bind(SayHelloFormatter) - ..bind(OneTimeDecorator); + ..bind(OneTimeDecorator) + ..bind(OuterShadowless) + ..bind(InnerShadowy); }); it('should select on element', async(() { @@ -652,7 +657,7 @@ void main() { scope.context['logger'] = logger; scope.context['once'] = null; var elts = es('{{logger("inner")}}'); - compile(elts, _.injector.get(DirectiveMap))(scope, _.directiveInjector, elts); + compile(elts, _.injector.get(DirectiveMap))(scope, null, elts); expect(logger).toEqual(['new']); expect(logger).toEqual(['new']); @@ -684,7 +689,7 @@ void main() { backend.whenGET('foo.html').respond('
WORKED
'); var elts = es(''); var scope = _.rootScope.createChild({}); - compile(elts, _.injector.get(DirectiveMap))(scope, _.directiveInjector, elts); + compile(elts, _.injector.get(DirectiveMap))(scope, null, elts); expect(logger).toEqual(['SimpleAttachComponent']); scope.destroy(); @@ -759,6 +764,13 @@ void main() { expect(_.rootElement.shadowRoot).toBeNull(); })); + it('should correctly interpolate shadowless components inside shadowy', async(() { + var element = _.compile('outer-text'); + microLeap(); + _.rootScope.apply(); + expect(element).toHaveText('inner-text'); + })); + it('should create other components with the default strategy', async((ComponentFactory factory) { _.compile(''); if (factory is TranscludingComponentFactory) { @@ -862,6 +874,14 @@ void main() { expect(log.result()).toEqual('TabComponent-0; LocalAttrDirective-0; PaneComponent-1; LocalAttrDirective-0; PaneComponent-2; LocalAttrDirective-0'); })); + it('should break injection at component injectors', async((Logger log) { + _.compile(''); + microLeap(); + // The child in the light dom should receive parent, + // while the child from the template should receive the app level null parent. + expect(log.result()).toEqual('got parent; null parent'); + })); + it('should use the correct parent injector', async((Logger log) { // Getting the parent offsets correct while descending the template is tricky. If we get it wrong, this // test case will create too many TabComponents. @@ -960,6 +980,23 @@ void main() { expect(decorator.valueDecorator).toEqual('worked'); }); }); + + describe('Injection accross application injection boundaries', () { + it('should create directive injectors for elements only', + async((TestBed _, Logger logger, CompilerConfig config) { + if (!config.elementProbeEnabled) return; + _.compile(''); + var directiveInjector = ngInjector(_.rootElement); + var lazyInjector = NgView.createChildInjectorWithReload( + _.injector, + [new Module()..bind(LazyPane)..bind(LazyPaneHelper)]); + var dirMap = lazyInjector.get(DirectiveMap); + ViewFactory viewFactory = _.compiler([new Element.tag('lazy-pane')], dirMap); + var childScope = _.rootScope.createChild({}); + viewFactory(childScope, directiveInjector); + expect(logger).toContain('LazyPane-0'); + })); + }); })); } @@ -998,6 +1035,19 @@ class TabComponent { } } +@Component( + selector: 'lazy-pane', + visibility: Directive.CHILDREN_VISIBILITY +) +class LazyPane { + int id = 0; + LazyPane(Logger logger, LazyPaneHelper lph, Scope scope) { + logger('LazyPane-${id++}'); + } +} + +class LazyPaneHelper {} + @Component(selector: 'pane') class PaneComponent { TabComponent tabComponent; @@ -1021,6 +1071,33 @@ class LocalAttrDirective { } } +@Decorator( + selector: 'parent', + visibility: Directive.CHILDREN_VISIBILITY +) +class Parent { + Parent(Logger log) {} +} + + +@Decorator( + selector: 'child', + visibility: Directive.CHILDREN_VISIBILITY +) +class Child { + Child(Parent p, Logger log) { + log(p == null ? 'null parent' : 'got parent'); + } +} + +@Component( + selector: 'child-tmp-cmp', + template: '' +) +class ChildTemplateComponent { + ChildTemplateComponent() {} +} + @Decorator( selector: '[simple-transclude-in-attach]', visibility: Visibility.LOCAL, @@ -1412,3 +1489,14 @@ class ScopeAwareComponent implements ScopeAware { log('Scope set'); } } + +@Component( + selector: 'outer-shadowless', + template: 'inner-text', + useShadowDom: false) +class OuterShadowless {} + +@Component( + selector: 'inner-shadowy', + template: '') +class InnerShadowy {} diff --git a/test/core_dom/directive_injector_spec.dart b/test/core_dom/directive_injector_spec.dart index bb8e3bda1..eac9b3b15 100644 --- a/test/core_dom/directive_injector_spec.dart +++ b/test/core_dom/directive_injector_spec.dart @@ -34,10 +34,8 @@ void main() { }); it('should return basic types', () { - expect(injector.appInjector).toBe(appInjector); expect(injector.scope).toBe(scope); expect(injector.get(Injector)).toBe(appInjector); - expect(injector.get(DirectiveInjector)).toBe(injector); expect(injector.get(Scope)).toBe(scope); expect(injector.get(Node)).toBe(div); expect(injector.get(Element)).toBe(div); diff --git a/test/core_dom/element_binder_builder_spec.dart b/test/core_dom/element_binder_builder_spec.dart index 72a45eb8e..d07498f85 100644 --- a/test/core_dom/element_binder_builder_spec.dart +++ b/test/core_dom/element_binder_builder_spec.dart @@ -30,9 +30,9 @@ main() => describe('ElementBinderBuilder', () { ..bind(_Structural); }); - beforeEach((DirectiveMap d, ElementBinderFactory f) { + beforeEach((DirectiveMap d, ElementBinderFactory f, Injector i) { directives = d; - b = f.builder(null, null); + b = f.builder(null, null, i); }); addDirective(selector) { diff --git a/test/core_dom/mustache_spec.dart b/test/core_dom/mustache_spec.dart index 92ce00d12..cdcc1f3ac 100644 --- a/test/core_dom/mustache_spec.dart +++ b/test/core_dom/mustache_spec.dart @@ -18,7 +18,7 @@ main() { var template = compile(element, directives); rootScope.context['name'] = 'OK'; - var view = template(rootScope, injector.get(DirectiveInjector)); + var view = template(rootScope, null); element = view.nodes; @@ -52,7 +52,7 @@ main() { rootScope.context['name'] = 'OK'; rootScope.context['age'] = 23; - var view = template(rootScope, injector.get(DirectiveInjector)); + var view = template(rootScope, null); element = view.nodes[0]; @@ -70,7 +70,7 @@ main() { rootScope.context['line1'] = 'L1'; rootScope.context['line2'] = 'L2'; - var view = template(rootScope, injector.get(DirectiveInjector)); + var view = template(rootScope, null); element = view.nodes[0]; @@ -84,7 +84,7 @@ main() { Injector injector, DirectiveMap directives) { var element = es('
{{"World" | hello}}
'); var template = compile(element, directives); - var view = template(rootScope, injector.get(DirectiveInjector)); + var view = template(rootScope, null); rootScope.apply(); element = view.nodes; diff --git a/test/core_dom/view_spec.dart b/test/core_dom/view_spec.dart index 7dafa88ed..7cb0498b9 100644 --- a/test/core_dom/view_spec.dart +++ b/test/core_dom/view_spec.dart @@ -77,13 +77,13 @@ main() { View createView(Injector injector, String html) { final scope = injector.get(Scope); final c = injector.get(Compiler); - return c(es(html), injector.get(DirectiveMap))(scope, injector.get(DirectiveInjector)); + return c(es(html), injector.get(DirectiveMap))(scope, null); } beforeEach((Injector injector, Profiler perf) { rootElement.innerHtml = ''; var scope = injector.get(Scope); - viewPort = new ViewPort(injector.get(DirectiveInjector), scope, rootElement.childNodes[0], + viewPort = new ViewPort(null, scope, rootElement.childNodes[0], injector.get(Animate)); a = createView(injector, "Aa"); b = createView(injector, "Bb"); @@ -223,7 +223,7 @@ main() { Compiler compiler = rootInjector.get(Compiler); DirectiveMap directives = rootInjector.get(DirectiveMap); - compiler(es('{{\'a\' | formatterA}}'), directives)(rootScope, rootInjector.get(DirectiveInjector)); + compiler(es('{{\'a\' | formatterA}}'), directives)(rootScope, null); rootScope.apply(); expect(log.log, equals(['AFormatter', 'ADirective'])); @@ -233,12 +233,12 @@ main() { ..bind(BFormatter) ..bind(BDirective); - var childInjector = forceNewDirectivesAndFormatters(rootInjector, null, [childModule]); + var childInjector = NgView.createChildInjectorWithReload(rootInjector, [childModule]); DirectiveMap newDirectives = childInjector.get(DirectiveMap); var scope = childInjector.get(Scope); compiler(es('{{\'a\' | formatterA}}' - '{{\'b\' | formatterB}}'), newDirectives)(scope, childInjector.get(DirectiveInjector)); + '{{\'b\' | formatterB}}'), newDirectives)(scope, null); rootScope.apply(); expect(log.log, equals(['AFormatter', 'ADirective', 'BFormatter', 'ADirective', 'BDirective'])); diff --git a/test/directive/ng_bind_html_spec.dart b/test/directive/ng_bind_html_spec.dart index 019987b04..1000f77f0 100644 --- a/test/directive/ng_bind_html_spec.dart +++ b/test/directive/ng_bind_html_spec.dart @@ -9,7 +9,7 @@ main() { it('should sanitize and set innerHtml and sanitize and set html', (Scope scope, Injector injector, Compiler compiler, DirectiveMap directives) { var element = es('
'); - compiler(element, directives)(scope, injector.get(DirectiveInjector), element); + compiler(element, directives)(scope, null, element); scope.context['htmlVar'] = 'Google!'; scope.apply(); // Sanitization removes the href attribute on the tag. @@ -28,7 +28,7 @@ main() { it('should use injected NodeValidator and override default sanitize behavior', (Scope scope, Injector injector, Compiler compiler, DirectiveMap directives) { var element = es('
'); - compiler(element, directives)(scope, injector.get(DirectiveInjector), element); + compiler(element, directives)(scope, null, element); scope.context['htmlVar'] = '
Google!'; scope.apply(); // Sanitation allows href attributes per injected sanitizer. diff --git a/test/directive/ng_bind_spec.dart b/test/directive/ng_bind_spec.dart index 3d7468acf..43f3945db 100644 --- a/test/directive/ng_bind_spec.dart +++ b/test/directive/ng_bind_spec.dart @@ -10,7 +10,7 @@ main() { it('should set.text', (Scope scope, Injector injector, Compiler compiler, DirectiveMap directives) { var element = e('
'); - compiler([element], directives)(scope, injector.get(DirectiveInjector), [element]); + compiler([element], directives)(scope, null, [element]); scope.context['a'] = "abc123"; scope.apply(); expect(element.text).toEqual('abc123'); diff --git a/test/directive/ng_form_spec.dart b/test/directive/ng_form_spec.dart index a0cb7624e..dd51734f4 100644 --- a/test/directive/ng_form_spec.dart +++ b/test/directive/ng_form_spec.dart @@ -724,7 +724,7 @@ void main() { it('should be resolvable by injector if configured by user.', (Scope scope, Injector injector, Compiler compiler, DirectiveMap directives) { var element = es('
'); - expect(() => compiler(element, directives)(scope, injector.get(DirectiveInjector), element)) + expect(() => compiler(element, directives)(scope, null, element)) .not.toThrow(); }); }); diff --git a/test/directive/ng_if_spec.dart b/test/directive/ng_if_spec.dart index ff016a9a9..2ad47f030 100644 --- a/test/directive/ng_if_spec.dart +++ b/test/directive/ng_if_spec.dart @@ -28,7 +28,7 @@ main() { logger = _logger; compile = (html, [applyFn]) { element = e(html); - compiler([element], _directives)(scope, injector.get(DirectiveInjector), [element]); + compiler([element], _directives)(scope, null, [element]); scope.apply(applyFn); }; directives = _directives; diff --git a/test/directive/ng_non_bindable_spec.dart b/test/directive/ng_non_bindable_spec.dart index 1a2817360..82b8c908f 100644 --- a/test/directive/ng_non_bindable_spec.dart +++ b/test/directive/ng_non_bindable_spec.dart @@ -19,7 +19,7 @@ main() { ' {{a}}' + ' ' + '
'); - compiler([element], directives)(scope, injector.get(DirectiveInjector), [element]); + compiler([element], directives)(scope, null, [element]); scope.context['a'] = "one"; scope.context['b'] = "two"; scope.apply(); diff --git a/test/directive/ng_repeat_spec.dart b/test/directive/ng_repeat_spec.dart index c4da255ad..a0a61f5ee 100644 --- a/test/directive/ng_repeat_spec.dart +++ b/test/directive/ng_repeat_spec.dart @@ -23,9 +23,9 @@ main() { ViewFactory viewFactory = compiler([element], _directives); Injector blockInjector = injector; if (scope != null) { - viewFactory.bind(injector.get(DirectiveInjector))(scope); + viewFactory.bind(null)(scope); } else { - viewFactory(rootScope, injector.get(DirectiveInjector), [element]); + viewFactory(rootScope, null, [element]); } return element; }; @@ -35,7 +35,7 @@ main() { it(r'should set create a list of items', (Scope scope, Compiler compiler, Injector injector) { var element = es('
{{item}}
'); ViewFactory viewFactory = compiler(element, directives); - View view = viewFactory(scope, injector.get(DirectiveInjector), element); + View view = viewFactory(scope, null, element); scope.context['items'] = ['a', 'b']; scope.apply(); expect(element).toHaveText('ab'); @@ -50,7 +50,7 @@ main() { }); var element = es('
{{item}}
'); ViewFactory viewFactory = compiler(element, directives); - View view = viewFactory(scope, injector.get(DirectiveInjector), element); + View view = viewFactory(scope, null, element); scope.apply(); expect(element).toHaveText('ab'); }); @@ -60,7 +60,7 @@ main() { (Scope scope, Compiler compiler, Injector injector) { var element = es('
{{item}}
'); ViewFactory viewFactory = compiler(element, directives); - View view = viewFactory(scope, injector.get(DirectiveInjector), element); + View view = viewFactory(scope, null, element); scope.context['items'] = ['a', 'b'].map((i) => i); // makes an iterable scope.apply(); expect(element).toHaveText('ab'); @@ -550,7 +550,7 @@ main() { compile = (html) { element = e(html); var viewFactory = compiler([element], _directives); - viewFactory(scope, child.get(DirectiveInjector), [element]); + viewFactory(scope, null, [element]); return element; }; directives = _directives; diff --git a/test/introspection_spec.dart b/test/introspection_spec.dart index a034d7073..dec25ca81 100644 --- a/test/introspection_spec.dart +++ b/test/introspection_spec.dart @@ -10,8 +10,8 @@ void main() { it('should retrieve ElementProbe', (TestBed _) { _.compile('
'); ElementProbe probe = ngProbe(_.rootElement); - expect(probe.injector.appInjector).toBe(_.injector); - expect(ngInjector(_.rootElement).appInjector).toBe(_.injector); + expect(probe.injector.get(Injector)).toBe(_.injector); + expect(ngInjector(_.rootElement).get(Injector)).toBe(_.injector); expect(probe.directives[0] is NgBind).toBe(true); expect(ngDirectives(_.rootElement)[0] is NgBind).toBe(true); expect(probe.scope).toBe(_.rootScope); diff --git a/test/routing/routing_spec.dart b/test/routing/routing_spec.dart index d0b8af223..f147bdb59 100644 --- a/test/routing/routing_spec.dart +++ b/test/routing/routing_spec.dart @@ -46,7 +46,7 @@ main() { initRouter(initializer) { var injector = applicationFactory() .addModule(new AngularMockModule()) - .addModule(new Module()..bind(RouteInitializerFn, toValue: initializer)) + .addModule(new Module()..bind(RouteInitializerFn, toValue: initializer)..bind(MyDirective)) .createInjector(); injector.get(NgRoutingHelper); // force routing initialization router = injector.get(Router); @@ -433,6 +433,33 @@ main() { expect(root.text).toEqual('Hello, World!'); })); + it('should pass the right injector into nested views', async(() { + initRouter((Router router, RouteViewFactory views) { + views.configure({ + 'foo': ngRoute( + path: '/foo', + view: 'foo.html', + mount: { + 'bar': ngRoute( + path: '/bar', + view: 'foo_bar.html') + }, + modules: () => [new Module()..bind(MyService)] + ) + }); + }); + var cache = _.injector.get(TemplateCache); + cache.put('foo.html', new HttpResponse(200, '')); + cache.put('foo_bar.html', new HttpResponse(200, '
')); + _.compile(''); + router.route('/foo/bar'); + microLeap(); + _.rootScope.apply(); + + Logger logger = _.injector.get(Logger); + expect(logger.length).toEqual(1); + expect(logger[0] is MyService).toEqual(true); + })); }); } @@ -451,6 +478,15 @@ class NewDirective { } } +@Decorator(selector: '[my-directive]') +class MyDirective { + MyDirective(MyService service, Logger logger) { + logger.add(service); + } +} + +class MyService {} + @Formatter(name:'hello') class HelloFormatter { String call(String str) {