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) {