diff --git a/benchmark/pubspec.lock b/benchmark/pubspec.lock index 4fceed7bd..70b9a773c 100644 --- a/benchmark/pubspec.lock +++ b/benchmark/pubspec.lock @@ -4,7 +4,7 @@ packages: analyzer: description: analyzer source: hosted - version: "0.13.6" + version: "0.15.7" angular: description: path: ".." @@ -30,15 +30,17 @@ packages: code_transformers: description: code_transformers source: hosted - version: "0.1.3" + version: "0.1.4+2" collection: description: collection source: hosted version: "0.9.2" di: - description: di - source: hosted - version: "1.0.0" + description: + path: "../../di.dart" + relative: true + source: path + version: "2.0.0-alpha.9" html5lib: description: html5lib source: hosted @@ -46,7 +48,7 @@ packages: intl: description: intl source: hosted - version: "0.9.9" + version: "0.9.10" logging: description: logging source: hosted diff --git a/benchmark/pubspec.yaml b/benchmark/pubspec.yaml index 141c3f449..ead62b1e6 100644 --- a/benchmark/pubspec.yaml +++ b/benchmark/pubspec.yaml @@ -13,3 +13,4 @@ transformers: - $dart2js: minify: false checked: false + commandLineOptions: [--dump-info] diff --git a/benchmark/web/tree.dart b/benchmark/web/tree.dart index b5dc8d8bc..cc96fa359 100644 --- a/benchmark/web/tree.dart +++ b/benchmark/web/tree.dart @@ -246,6 +246,7 @@ class NgFreeTreeClass implements ShadowRootAware { // Main function runs the benchmark. main() { + setupModuleTypeReflector(); var cleanup, createDom; var module = new Module() diff --git a/bin/parser_generator_for_spec.dart b/bin/parser_generator_for_spec.dart index 2a15500c1..e35c98b68 100644 --- a/bin/parser_generator_for_spec.dart +++ b/bin/parser_generator_for_spec.dart @@ -1,7 +1,6 @@ import 'dart:io' as io; import 'package:di/di.dart'; -import 'package:di/dynamic_injector.dart'; import 'package:angular/cache/module.dart'; import 'package:angular/core/parser/lexer.dart'; import 'package:angular/core/parser/parser.dart'; @@ -14,8 +13,7 @@ main(arguments) { ..bind(Parser, toImplementation: DynamicParser) ..install(new CacheModule()); module.bind(ParserBackend, toImplementation: DartGetterSetterGen); - Injector injector = new DynamicInjector(modules: [module], - allowImplicitInjection: true); + Injector injector = new ModuleInjector([module]); // List generated using: // node node_modules/karma/bin/karma run | grep -Eo ":XNAY:.*:XNAY:" | sed -e 's/:XNAY://g' | sed -e "s/^/'/" | sed -e "s/$/',/" | sort | uniq > missing_expressions diff --git a/lib/angular.dart b/lib/angular.dart index e17923a50..d79b93b3d 100644 --- a/lib/angular.dart +++ b/lib/angular.dart @@ -9,6 +9,6 @@ export 'package:angular/introspection.dart' hide elementExpando, publishToJavaScript; export 'package:angular/formatter/module.dart'; export 'package:angular/routing/module.dart'; -export 'package:di/di.dart' hide lastKeyId; +export 'package:di/di.dart'; +export 'package:di/annotations.dart'; export 'package:route_hierarchical/client.dart' hide childRoute; - diff --git a/lib/animate/module.dart b/lib/animate/module.dart index d7d68ee5a..5552cc72a 100644 --- a/lib/animate/module.dart +++ b/lib/animate/module.dart @@ -108,6 +108,7 @@ import 'package:angular/core_dom/dom_util.dart' as util; import 'package:logging/logging.dart'; import 'package:perf_api/perf_api.dart'; import 'package:di/di.dart'; +import 'package:di/annotations.dart'; @MirrorsUsed(targets: const [ 'angular.animate' diff --git a/lib/application.dart b/lib/application.dart index fe3d62ea8..5c6cf10bf 100644 --- a/lib/application.dart +++ b/lib/application.dart @@ -84,6 +84,7 @@ import 'package:angular/routing/module.dart'; import 'package:angular/introspection.dart'; import 'package:angular/core_dom/static_keys.dart'; +import 'package:angular/core_dom/directive_injector.dart'; /** * This is the top level module which describes all Angular components, @@ -96,6 +97,7 @@ import 'package:angular/core_dom/static_keys.dart'; */ class AngularModule extends Module { AngularModule() { + DirectiveInjector.initUID(); install(new CacheModule()); install(new CoreModule()); install(new CoreDomModule()); @@ -105,7 +107,6 @@ class AngularModule extends Module { install(new PerfModule()); install(new RoutingModule()); - bind(MetadataExtractor); bind(Expando, toValue: elementExpando); } } @@ -175,9 +176,11 @@ abstract class Application { injector.getByKey(JS_CACHE_REGISTER_KEY); initializeDateFormatting(null, null).then((_) { try { - var compiler = injector.getByKey(COMPILER_KEY); - var viewFactory = compiler(rootElements, injector.getByKey(DIRECTIVE_MAP_KEY)); - viewFactory(injector, rootElements); + Compiler compiler = injector.getByKey(COMPILER_KEY); + 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); } catch (e, s) { exceptionHandler(e, s); } @@ -190,5 +193,5 @@ abstract class Application { * Creates an injector function that can be used for retrieving services as well as for * dependency injection. */ - Injector createInjector(); + Injector createInjector() => new ModuleInjector(modules); } diff --git a/lib/application_factory.dart b/lib/application_factory.dart index 25b95cd40..0baa76921 100644 --- a/lib/application_factory.dart +++ b/lib/application_factory.dart @@ -9,7 +9,6 @@ */ library angular.app.factory; -import 'package:di/dynamic_injector.dart'; import 'package:angular/angular.dart'; import 'package:angular/core/registry.dart'; import 'package:angular/core/parser/parser.dart' show ClosureMap; @@ -64,8 +63,6 @@ class _DynamicApplication extends Application { ..bind(FieldGetterFactory, toImplementation: DynamicFieldGetterFactory) ..bind(ClosureMap, toImplementation: DynamicClosureMap); } - - Injector createInjector() => new DynamicInjector(modules: modules); } /** diff --git a/lib/application_factory_static.dart b/lib/application_factory_static.dart index 3379177ff..a44c3f490 100644 --- a/lib/application_factory_static.dart +++ b/lib/application_factory_static.dart @@ -30,8 +30,6 @@ */ library angular.app.factory.static; -import 'package:di/static_injector.dart'; -import 'package:di/di.dart' show TypeFactory, Injector; import 'package:angular/application.dart'; import 'package:angular/core/registry.dart'; import 'package:angular/core/parser/parser.dart'; @@ -46,9 +44,7 @@ export 'package:angular/change_detection/change_detection.dart' show FieldSetter; class _StaticApplication extends Application { - final Map typeFactories; - - _StaticApplication(Map this.typeFactories, + _StaticApplication( Map metadata, Map fieldGetters, Map fieldSetters, @@ -58,9 +54,6 @@ class _StaticApplication extends Application { ..bind(FieldGetterFactory, toValue: new StaticFieldGetterFactory(fieldGetters)) ..bind(ClosureMap, toValue: new StaticClosureMap(fieldGetters, fieldSetters, symbols)); } - - Injector createInjector() => - new StaticInjector(modules: modules, typeFactories: typeFactories); } /** @@ -81,20 +74,19 @@ class _StaticApplication extends Application { * becomes: * * main() { - * staticApplication(generated_static_injector.factories, - * generated_static_metadata.typeAnnotations, - * generated_static_expressions.getters, - * generated_static_expressions.setters, - * generated_static_expressions.symbols) - * .addModule(new Module()..bind(HelloWorldController)) - * .run(); + * staticApplication( + * generated_static_metadata.typeAnnotations, + * generated_static_expressions.getters, + * generated_static_expressions.setters, + * generated_static_expressions.symbols) + * .addModule(new Module()..bind(HelloWorldController)) + * .run(); * */ Application staticApplicationFactory( - Map typeFactories, Map metadata, Map fieldGetters, Map fieldSetters, Map symbols) { - return new _StaticApplication(typeFactories, metadata, fieldGetters, fieldSetters, symbols); + return new _StaticApplication(metadata, fieldGetters, fieldSetters, symbols); } diff --git a/lib/cache/js_cache_register.dart b/lib/cache/js_cache_register.dart index 76c086928..ce54a499f 100644 --- a/lib/cache/js_cache_register.dart +++ b/lib/cache/js_cache_register.dart @@ -4,6 +4,7 @@ library angular.cache.js; import 'dart:js' as js; import 'package:di/di.dart'; +import 'package:di/annotations.dart'; import 'package:angular/core/annotation_src.dart'; import 'package:angular/cache/module.dart'; diff --git a/lib/cache/module.dart b/lib/cache/module.dart index 51cbfaab4..6296700fd 100644 --- a/lib/cache/module.dart +++ b/lib/cache/module.dart @@ -4,7 +4,7 @@ import 'dart:collection'; import 'dart:async'; import 'package:di/di.dart'; -import 'package:angular/core/annotation_src.dart'; +import 'package:di/annotations.dart'; part "cache.dart"; part "cache_register.dart"; @@ -13,4 +13,7 @@ class CacheModule extends Module { CacheModule() { bind(CacheRegister); } + CacheModule.withReflector(reflector): super.withReflector(reflector) { + bind(CacheRegister); + } } diff --git a/lib/change_detection/ast_parser.dart b/lib/change_detection/ast_parser.dart index 46129ee5a..898faa9c3 100644 --- a/lib/change_detection/ast_parser.dart +++ b/lib/change_detection/ast_parser.dart @@ -2,6 +2,7 @@ library angular.change_detection.ast_parser; import 'dart:collection'; +import 'package:di/annotations.dart'; import 'package:angular/core/parser/syntax.dart' as syntax; import 'package:angular/core/parser/parser.dart'; import 'package:angular/core/formatter.dart'; diff --git a/lib/core/annotation.dart b/lib/core/annotation.dart index f8fe90ad6..cee2ad10f 100644 --- a/lib/core/annotation.dart +++ b/lib/core/annotation.dart @@ -13,12 +13,12 @@ export "package:angular/core/annotation_src.dart" show Formatter, DirectiveBinder, DirectiveBinderFn, - Injectable, Directive, Component, Controller, Decorator, + Visibility, DirectiveAnnotation, NgAttr, diff --git a/lib/core/annotation_src.dart b/lib/core/annotation_src.dart index bb0da9c29..529397587 100644 --- a/lib/core/annotation_src.dart +++ b/lib/core/annotation_src.dart @@ -1,43 +1,28 @@ library angular.core.annotation_src; -import "package:di/di.dart" show Injector, Visibility; +import "package:di/di.dart" show Injector, Visibility, Factory; abstract class DirectiveBinder { - bind(key, {Function toFactory, inject, - Visibility visibility: Directive.CHILDREN_VISIBILITY}); + bind(key, {Function toFactory, inject, Visibility visibility: Visibility.CHILDREN}); } typedef void DirectiveBinderFn(DirectiveBinder module); RegExp _ATTR_NAME = new RegExp(r'\[([^\]]+)\]$'); -const String SHADOW_DOM_INJECTOR_NAME = 'SHADOW_INJECTOR'; - -skipShadow(Injector injector) - => injector.name == SHADOW_DOM_INJECTOR_NAME ? injector.parent : injector; - -localVisibility (Injector requesting, Injector defining) - => identical(skipShadow(requesting), defining); - -directChildrenVisibility(Injector requesting, Injector defining) { - requesting = skipShadow(requesting); - return identical(requesting.parent, defining) || localVisibility(requesting, defining); -} - Directive cloneWithNewMap(Directive annotation, map) => annotation._cloneWithNewMap(map); String mappingSpec(DirectiveAnnotation annotation) => annotation._mappingSpec; +class Visibility { + static const LOCAL = const Visibility._('LOCAL'); + static const CHILDREN = const Visibility._('CHILDREN'); + static const DIRECT_CHILD = const Visibility._('DIRECT_CHILD'); -/** - * An annotation when applied to a class indicates that the class (service) will - * be instantiated by di injector. This annotation is also used to designate which - * classes need to have a static factory generated when using static angular, and - * therefore is required on any injectable class. - */ -class Injectable { - const Injectable(); + final String name; + const Visibility._(this.name); + toString() => 'Visibility: $name'; } /** @@ -46,16 +31,19 @@ class Injectable { abstract class Directive { /// The directive can only be injected to other directives on the same element. - static const Visibility LOCAL_VISIBILITY = localVisibility; + @deprecated // ('Use Visibility.LOCAL instead') + static const Visibility LOCAL_VISIBILITY = Visibility.LOCAL; /// The directive can be injected to other directives on the same or child elements. - static const Visibility CHILDREN_VISIBILITY = null; + @deprecated// ('Use Visibility.CHILDREN instead') + static const Visibility CHILDREN_VISIBILITY = Visibility.CHILDREN; /** * The directive on this element can only be injected to other directives * declared on elements which are direct children of the current element. */ - static const Visibility DIRECT_CHILDREN_VISIBILITY = directChildrenVisibility; + @deprecated// ('Use Visibility.DIRECT_CHILD instead') + static const Visibility DIRECT_CHILDREN_VISIBILITY = Visibility.DIRECT_CHILD; /** * CSS selector which will trigger this component/directive. @@ -129,8 +117,8 @@ abstract class Directive { * selector: '[foo]', * module: Foo.moduleFactory) * class Foo { - * static moduleFactory() => new Module() - * ..bind(SomeTypeA, visibility: Directive.LOCAL_VISIBILITY); + * static moduleFactory(DirectiveBinder binder) => + * binder.bind(SomeTypeA, visibility: Directive.LOCAL_VISIBILITY); * } * * When specifying types, factories or values in the module, notice that @@ -139,7 +127,7 @@ abstract class Directive { * * [Directive.CHILDREN_VISIBILITY] * * [Directive.DIRECT_CHILDREN_VISIBILITY] */ - final Function module; + final DirectiveBinderFn module; /** * Use map to define the mapping of DOM attributes to fields. @@ -222,8 +210,8 @@ abstract class Directive { const Directive({ this.selector, - this.children: Directive.COMPILE_CHILDREN, - this.visibility: Directive.LOCAL_VISIBILITY, + this.children, + this.visibility, this.module, this.map: const {}, this.exportExpressions: const [], @@ -231,10 +219,6 @@ abstract class Directive { }); toString() => selector; - get hashCode => selector.hashCode; - operator==(other) => - other is Directive && selector == other.selector; - Directive _cloneWithNewMap(newMap); } @@ -335,7 +319,7 @@ class Component extends Directive { applyAuthorStyles, resetStyleInheritance, this.publishAs, - module, + DirectiveBinderFn module, map, selector, visibility, @@ -393,7 +377,7 @@ class Decorator extends Directive { const Decorator({children: Directive.COMPILE_CHILDREN, map, selector, - module, + DirectiveBinderFn module, visibility, exportExpressions, exportExpressionAttrs}) @@ -446,7 +430,7 @@ class Controller extends Decorator { children: Directive.COMPILE_CHILDREN, this.publishAs, map, - module, + DirectiveBinderFn module, selector, visibility, exportExpressions, @@ -595,8 +579,5 @@ class Formatter { const Formatter({this.name}); - int get hashCode => name.hashCode; - bool operator==(other) => name == other.name; - toString() => 'Formatter: $name'; } diff --git a/lib/core/formatter.dart b/lib/core/formatter.dart index 21577a217..484282b67 100644 --- a/lib/core/formatter.dart +++ b/lib/core/formatter.dart @@ -2,6 +2,7 @@ library angular.core_internal.formatter_map; import 'dart:collection'; import 'package:di/di.dart'; +import 'package:di/annotations.dart'; import 'package:angular/core/annotation_src.dart'; import 'package:angular/core/registry.dart'; @@ -14,7 +15,7 @@ class FormatterMap { final Injector _injector; FormatterMap(this._injector, MetadataExtractor extractMetadata) { - _injector.types.forEach((type) { + (_injector as ModuleInjector).types.forEach((type) { extractMetadata(type) .where((annotation) => annotation is Formatter) .forEach((Formatter formatter) { diff --git a/lib/core/module.dart b/lib/core/module.dart index 0029d5dea..55d8a30b7 100644 --- a/lib/core/module.dart +++ b/lib/core/module.dart @@ -26,6 +26,9 @@ export "package:angular/cache/module.dart" show CacheRegister, CacheRegisterStats; +export "package:angular/core_dom/directive_injector.dart" show + DirectiveInjector; + export "package:angular/core_dom/module_internal.dart" show Animation, AnimationResult, diff --git a/lib/core/module_internal.dart b/lib/core/module_internal.dart index a85dfc12f..7ead73d8a 100644 --- a/lib/core/module_internal.dart +++ b/lib/core/module_internal.dart @@ -6,6 +6,7 @@ import 'dart:math'; import 'package:intl/intl.dart'; import 'package:di/di.dart'; +import 'package:di/annotations.dart'; import 'package:angular/core/parser/parser.dart'; import 'package:angular/core/parser/lexer.dart'; diff --git a/lib/core/parser/dynamic_parser.dart b/lib/core/parser/dynamic_parser.dart index 26486b15c..d1caf80f2 100644 --- a/lib/core/parser/dynamic_parser.dart +++ b/lib/core/parser/dynamic_parser.dart @@ -1,5 +1,6 @@ library angular.core.parser.dynamic_parser; +import 'package:di/annotations.dart'; import 'package:angular/cache/module.dart'; import 'package:angular/core/annotation_src.dart' hide Formatter; import 'package:angular/core/module_internal.dart' show FormatterMap; diff --git a/lib/core/parser/lexer.dart b/lib/core/parser/lexer.dart index 41c96c3a8..46abe864a 100644 --- a/lib/core/parser/lexer.dart +++ b/lib/core/parser/lexer.dart @@ -1,5 +1,6 @@ library angular.core.parser.lexer; +import 'package:di/annotations.dart'; import 'package:angular/core/annotation_src.dart'; import 'package:angular/core/parser/characters.dart'; diff --git a/lib/core/registry_static.dart b/lib/core/registry_static.dart index a6758a5ba..426b26aa6 100644 --- a/lib/core/registry_static.dart +++ b/lib/core/registry_static.dart @@ -1,6 +1,6 @@ library angular.core_static; -import 'package:angular/core/annotation_src.dart'; +import 'package:di/annotations.dart'; import 'package:angular/core/registry.dart'; @Injectable() diff --git a/lib/core/static_keys.dart b/lib/core/static_keys.dart index f133735a6..fe29b88ee 100644 --- a/lib/core/static_keys.dart +++ b/lib/core/static_keys.dart @@ -3,9 +3,10 @@ library angular.static_keys; import 'package:di/di.dart'; import 'package:angular/core/module_internal.dart'; +final Key INJECTOR_KEY = new Key(Injector); final Key EXCEPTION_HANDLER_KEY = new Key(ExceptionHandler); final Key ROOT_SCOPE_KEY = new Key(RootScope); final Key SCOPE_KEY = new Key(Scope); final Key SCOPE_STATS_CONFIG_KEY = new Key(ScopeStatsConfig); final Key FORMATTER_MAP_KEY = new Key(FormatterMap); -final Key INTERPOLATE_KEY = new Key(Interpolate); \ No newline at end of file +final Key INTERPOLATE_KEY = new Key(Interpolate); diff --git a/lib/core_dom/common.dart b/lib/core_dom/common.dart index d109da1d1..24905af25 100644 --- a/lib/core_dom/common.dart +++ b/lib/core_dom/common.dart @@ -6,24 +6,32 @@ List cloneElements(elements) { class MappingParts { final String attrName; + final String bindAttrName; final AST attrValueAST; final String mode; final AST dstAST; final String originalValue; - const MappingParts(this.attrName, this.attrValueAST, this.mode, this.dstAST, this.originalValue); + MappingParts(attrName, this.attrValueAST, this.mode, this.dstAST, this.originalValue) + : attrName = attrName, + bindAttrName = "bind-" + attrName; } class DirectiveRef { final dom.Node element; final Type type; + final Function factory; + final List paramKeys; final Key typeKey; final Directive annotation; final String value; final AST valueAST; final mappings = new List(); - DirectiveRef(this.element, this.type, this.annotation, this.typeKey, [ this.value, this.valueAST ]); + DirectiveRef(this.element, type, this.annotation, this.typeKey, [ this.value, this.valueAST ]) + : type = type, + factory = Module.DEFAULT_REFLECTOR.factoryFor(type), + paramKeys = Module.DEFAULT_REFLECTOR.parameterKeysFor(type); String toString() { var html = element is dom.Element @@ -39,13 +47,17 @@ class DirectiveRef { * Creates a child injector that allows loading new directives, formatters and * services from the provided modules. */ -Injector forceNewDirectivesAndFormatters(Injector injector, List modules) { +Injector forceNewDirectivesAndFormatters(Injector injector, DirectiveInjector dirInjector, + List modules) { modules.add(new Module() - ..bind(Scope, toFactory: (Injector i) { - var scope = i.parent.getByKey(SCOPE_KEY); - return scope.createChild(new PrototypeMap(scope.context)); - }, inject: [Injector])); - - return injector.createChild(modules, - forceNewInstances: [DirectiveMap, FormatterMap]); + ..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 new file mode 100644 index 000000000..3b8dda2f7 --- /dev/null +++ b/lib/core_dom/directive_injector.dart @@ -0,0 +1,435 @@ +library angular.node_injector; + +import 'dart:collection'; +import 'dart:html' show Node, Element, ShadowRoot; +import 'dart:profiler'; + +import 'package:di/di.dart'; +import 'package:di/annotations.dart'; +import 'package:di/src/module.dart' show DEFAULT_VALUE, Binding; +import 'package:angular/core/static_keys.dart'; +import 'package:angular/core_dom/static_keys.dart'; + +import 'package:angular/core/module.dart' show Scope, RootScope; +import 'package:angular/core/annotation.dart' show Visibility, DirectiveBinder; +import 'package:angular/core_dom/module_internal.dart' + show Animate, View, ViewFactory, BoundViewFactory, ViewPort, NodeAttrs, ElementProbe, + NgElement, ContentPort, TemplateLoader, ShadowRootEventHandler, EventHandler; + +var _TAG_GET = new UserTag('DirectiveInjector.get()'); +var _TAG_INSTANTIATE = new UserTag('DirectiveInjector.instantiate()'); + +final DIRECTIVE_INJECTOR_KEY = new Key(DirectiveInjector); +final CONTENT_PORT_KEY = new Key(ContentPort); +final TEMPLATE_LOADER_KEY = new Key(TemplateLoader); +final SHADOW_ROOT_KEY = new Key(ShadowRoot); + +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; +const int DIRECTIVE_INJECTOR_KEY_ID = 2; +const int NODE_KEY_ID = 3; +const int ELEMENT_KEY_ID = 4; +const int NODE_ATTRS_KEY_ID = 5; +const int ANIMATE_KEY_ID = 6; +const int SCOPE_KEY_ID = 7; +const int VIEW_KEY_ID = 8; +const int VIEW_PORT_KEY_ID = 9; +const int VIEW_FACTORY_KEY_ID = 10; +const int NG_ELEMENT_KEY_ID = 11; +const int BOUND_VIEW_FACTORY_KEY_ID = 12; +const int ELEMENT_PROBE_KEY_ID = 13; +const int TEMPLATE_LOADER_KEY_ID = 14; +const int SHADOW_ROOT_KEY_ID = 15; +const int CONTENT_PORT_KEY_ID = 16; +const int EVENT_HANDLER_KEY_ID = 17; +const int KEEP_ME_LAST = 18; + +class DirectiveInjector implements DirectiveBinder { + static bool _isInit = false; + static initUID() { + if (_isInit) return; + _isInit = true; + INJECTOR_KEY.uid = INJECTOR_KEY_ID; + DIRECTIVE_INJECTOR_KEY.uid = DIRECTIVE_INJECTOR_KEY_ID; + NODE_KEY.uid = NODE_KEY_ID; + ELEMENT_KEY.uid = ELEMENT_KEY_ID; + NODE_ATTRS_KEY.uid = NODE_ATTRS_KEY_ID; + SCOPE_KEY.uid = SCOPE_KEY_ID; + VIEW_KEY.uid = VIEW_KEY_ID; + VIEW_PORT_KEY.uid = VIEW_PORT_KEY_ID; + VIEW_FACTORY_KEY.uid = VIEW_FACTORY_KEY_ID; + NG_ELEMENT_KEY.uid = NG_ELEMENT_KEY_ID; + BOUND_VIEW_FACTORY_KEY.uid = BOUND_VIEW_FACTORY_KEY_ID; + ELEMENT_PROBE_KEY.uid = ELEMENT_PROBE_KEY_ID; + TEMPLATE_LOADER_KEY.uid = TEMPLATE_LOADER_KEY_ID; + SHADOW_ROOT_KEY.uid = SHADOW_ROOT_KEY_ID; + CONTENT_PORT_KEY.uid = CONTENT_PORT_KEY_ID; + EVENT_HANDLER_KEY.uid = EVENT_HANDLER_KEY_ID; + ANIMATE_KEY.uid = ANIMATE_KEY_ID; + for(var i = 1; i < KEEP_ME_LAST; i++) { + if (_KEYS[i].uid != i) throw 'MISSORDERED KEYS ARRAY: ${_KEYS} at $i'; + } + } + static List _KEYS = + [ UNDEFINED_ID + , INJECTOR_KEY + , DIRECTIVE_INJECTOR_KEY + , NODE_KEY + , ELEMENT_KEY + , NODE_ATTRS_KEY + , ANIMATE_KEY + , SCOPE_KEY + , VIEW_KEY + , VIEW_PORT_KEY + , VIEW_FACTORY_KEY + , NG_ELEMENT_KEY + , BOUND_VIEW_FACTORY_KEY + , ELEMENT_PROBE_KEY + , TEMPLATE_LOADER_KEY + , SHADOW_ROOT_KEY + , CONTENT_PORT_KEY + , EVENT_HANDLER_KEY + , KEEP_ME_LAST + ]; + + final DirectiveInjector parent; + final Injector appInjector; + final Node _node; + final NodeAttrs _nodeAttrs; + final Animate _animate; + final EventHandler _eventHandler; + Scope scope; //TODO(misko): this should be final after we get rid of controller + + NgElement _ngElement; + ElementProbe _elementProbe; + + Key _key0 = null; dynamic _obj0; List _pKeys0; Function _factory0; + Key _key1 = null; dynamic _obj1; List _pKeys1; Function _factory1; + Key _key2 = null; dynamic _obj2; List _pKeys2; Function _factory2; + Key _key3 = null; dynamic _obj3; List _pKeys3; Function _factory3; + Key _key4 = null; dynamic _obj4; List _pKeys4; Function _factory4; + Key _key5 = null; dynamic _obj5; List _pKeys5; Function _factory5; + Key _key6 = null; dynamic _obj6; List _pKeys6; Function _factory6; + Key _key7 = null; dynamic _obj7; List _pKeys7; Function _factory7; + Key _key8 = null; dynamic _obj8; List _pKeys8; Function _factory8; + Key _key9 = null; dynamic _obj9; List _pKeys9; Function _factory9; + + static _toVisId(Visibility v) => identical(v, Visibility.LOCAL) + ? VISIBILITY_LOCAL + : (identical(v, Visibility.CHILDREN) ? VISIBILITY_CHILDREN : VISIBILITY_DIRECT_CHILD); + + static _toVis(int id) { + switch (id) { + 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 _temp_binding = new Binding(); + + DirectiveInjector(parent, appInjector, this._node, this._nodeAttrs, this._eventHandler, + this.scope, this._animate) + : appInjector = appInjector, + parent = parent == null ? new DefaultDirectiveInjector(appInjector) : parent; + + DirectiveInjector._default(this.parent, this.appInjector) + : _node = null, + _nodeAttrs = null, + _eventHandler = null, + scope = null, + _animate = null; + + bind(key, {dynamic toValue: DEFAULT_VALUE, + Function toFactory: DEFAULT_VALUE, + Type toImplementation, inject: const[], + Visibility visibility: Visibility.LOCAL}) { + if (key == null) throw 'Key is required'; + if (key is! Key) key = new Key(key); + if (inject is! List) inject = [inject]; + + _temp_binding.bind(key, Module.DEFAULT_REFLECTOR, toValue: toValue, toFactory: toFactory, + toImplementation: toImplementation, inject: inject); + + bindByKey(key, _temp_binding.factory, _temp_binding.parameterKeys, visibility); + } + + bindByKey(Key key, Function factory, List parameterKeys, [Visibility visibility]) { + if (visibility == null) visibility = Visibility.CHILDREN; + int visibilityId = _toVisId(visibility); + int keyVisId = key.uid; + if (keyVisId != visibilityId) { + if (keyVisId == null) { + key.uid = visibilityId; + } else { + throw "Can not set $visibility on $key, it alread has ${_toVis(keyVisId)}"; + } + } + if (_key0 == null || identical(_key0, key)) { _key0 = key; _pKeys0 = parameterKeys; _factory0 = factory; } + else if (_key1 == null || identical(_key1, key)) { _key1 = key; _pKeys1 = parameterKeys; _factory1 = factory; } + else if (_key2 == null || identical(_key2, key)) { _key2 = key; _pKeys2 = parameterKeys; _factory2 = factory; } + else if (_key3 == null || identical(_key3, key)) { _key3 = key; _pKeys3 = parameterKeys; _factory3 = factory; } + else if (_key4 == null || identical(_key4, key)) { _key4 = key; _pKeys4 = parameterKeys; _factory4 = factory; } + else if (_key5 == null || identical(_key5, key)) { _key5 = key; _pKeys5 = parameterKeys; _factory5 = factory; } + else if (_key6 == null || identical(_key6, key)) { _key6 = key; _pKeys6 = parameterKeys; _factory6 = factory; } + else if (_key7 == null || identical(_key7, key)) { _key7 = key; _pKeys7 = parameterKeys; _factory7 = factory; } + else if (_key8 == null || identical(_key8, key)) { _key8 = key; _pKeys8 = parameterKeys; _factory8 = factory; } + else if (_key9 == null || identical(_key9, key)) { _key9 = key; _pKeys9 = parameterKeys; _factory9 = factory; } + else { throw 'Maximum number of directives per element reached.'; } + } + + Object get(Type type) => getByKey(new Key(type)); + + Object getByKey(Key key) { + var oldTag = _TAG_GET.makeCurrent(); + try { + return _getByKey(key); + } on ResolvingError catch (e, s) { + e.appendKey(key); + rethrow; + } finally { + oldTag.makeCurrent(); + } + } + + Object _getByKey(Key key) { + 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); + } + + _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); + default: throw null; + } + } + + List get directives { + var directives = []; + if (_obj0 != null) directives.add(_obj0); + if (_obj1 != null) directives.add(_obj1); + if (_obj2 != null) directives.add(_obj2); + if (_obj3 != null) directives.add(_obj3); + if (_obj4 != null) directives.add(_obj4); + if (_obj5 != null) directives.add(_obj5); + if (_obj6 != null) directives.add(_obj6); + if (_obj7 != null) directives.add(_obj7); + if (_obj8 != null) directives.add(_obj8); + if (_obj9 != null) directives.add(_obj9); + return directives; + } + + Object _getById(int keyId) { + switch(keyId) { + 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; + case NODE_ATTRS_KEY_ID: return _nodeAttrs; + case ANIMATE_KEY_ID: return _animate; + case SCOPE_KEY_ID: return scope; + 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); + default: new NoProviderError(_KEYS[keyId]); + } + } + + dynamic _new(List paramKeys, Function fn) { + var oldTag = _TAG_GET.makeCurrent(); + int size = paramKeys.length; + var obj; + if (size > 15) { + var params = new List(paramKeys.length); + for(var i = 0; i < paramKeys.length; i++) { + params[i] = _getByKey(paramKeys[i]); + } + _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; + _TAG_INSTANTIATE.makeCurrent(); + switch(size) { + case 00: obj = fn(); break; + case 01: obj = fn(a01); break; + case 02: obj = fn(a01, a02); break; + case 03: obj = fn(a01, a02, a03); break; + case 04: obj = fn(a01, a02, a03, a04); break; + case 05: obj = fn(a01, a02, a03, a04, a05); break; + case 06: obj = fn(a01, a02, a03, a04, a05, a06); break; + case 07: obj = fn(a01, a02, a03, a04, a05, a06, a07); break; + case 08: obj = fn(a01, a02, a03, a04, a05, a06, a07, a08); break; + case 09: obj = fn(a01, a02, a03, a04, a05, a06, a07, a08, a09); break; + case 10: obj = fn(a01, a02, a03, a04, a05, a06, a07, a08, a09, a10); break; + case 11: obj = fn(a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11); break; + case 12: obj = fn(a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12); break; + case 13: obj = fn(a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13); break; + case 14: obj = fn(a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14); break; + case 15: obj = fn(a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15); + } + } + oldTag.makeCurrent(); + return obj; + } + + + ElementProbe get elementProbe { + if (_elementProbe == null) { + ElementProbe parentProbe = parent is DirectiveInjector ? parent.elementProbe : null; + _elementProbe = new ElementProbe(parentProbe, _node, this, scope); + } + return _elementProbe; + } + + NgElement get ngElement { + if (_ngElement == null) { + _ngElement = new NgElement(_node, scope, _animate); + } + return _ngElement; + } +} + +class TemplateDirectiveInjector extends DirectiveInjector { + final ViewFactory _viewFactory; + ViewPort _viewPort; + BoundViewFactory _boundViewFactory; + + TemplateDirectiveInjector(DirectiveInjector parent, Injector appInjector, + Node node, NodeAttrs nodeAttrs, EventHandler eventHandler, + Scope scope, Animate animate, this._viewFactory) + : super(parent, appInjector, node, nodeAttrs, eventHandler, scope, animate); + + + Object _getById(int keyId) { + switch(keyId) { + case VIEW_FACTORY_KEY_ID: return _viewFactory; + case VIEW_PORT_KEY_ID: return ((_viewPort) == null) ? + _viewPort = new ViewPort(this, scope, _node, _animate) : _viewPort; + case BOUND_VIEW_FACTORY_KEY_ID: return (_boundViewFactory == null) ? + _boundViewFactory = _viewFactory.bind(this.parent) : _boundViewFactory; + default: return super._getById(keyId); + } + } + +} + +abstract class ComponentDirectiveInjector extends DirectiveInjector { + + final TemplateLoader _templateLoader; + final ShadowRoot _shadowRoot; + + ComponentDirectiveInjector(DirectiveInjector parent, Injector appInjector, + EventHandler eventHandler, Scope scope, + this._templateLoader, this._shadowRoot) + : super(parent, appInjector, parent._node, parent._nodeAttrs, eventHandler, scope, + parent._animate); + + Object _getById(int keyId) { + switch(keyId) { + case TEMPLATE_LOADER_KEY_ID: return _templateLoader; + case SHADOW_ROOT_KEY_ID: return _shadowRoot; + default: return super._getById(keyId); + } + } + + _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 is DirectiveInjector ? parent.elementProbe : parent.getByKey(ELEMENT_PROBE_KEY); + _elementProbe = new ElementProbe(parentProbe, _shadowRoot, this, scope); + } + 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]); + } + } +} diff --git a/lib/core_dom/directive_map.dart b/lib/core_dom/directive_map.dart index 5bec04003..12a195c5a 100644 --- a/lib/core_dom/directive_map.dart +++ b/lib/core_dom/directive_map.dart @@ -18,7 +18,7 @@ class DirectiveMap { this._formatters, MetadataExtractor metadataExtractor, this._directiveSelectorFactory) { - injector.types.forEach((type) { + (injector as ModuleInjector).types.forEach((type) { metadataExtractor(type) .where((annotation) => annotation is Directive) .forEach((Directive directive) { diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart index c0d6f9137..55e4cd8cd 100644 --- a/lib/core_dom/element_binder.dart +++ b/lib/core_dom/element_binder.dart @@ -21,16 +21,6 @@ class TemplateElementBinder extends ElementBinder { null, null, onEvents, bindAttrs, childMode); String toString() => "[TemplateElementBinder template:$template]"; - - _registerViewFactory(node, parentInjector, nodeModule) { - assert(templateViewFactory != null); - nodeModule - ..bindByKey(VIEW_PORT_KEY, inject: const [], toFactory: () => - new ViewPort(node, parentInjector.getByKey(ANIMATE_KEY))) - ..bindByKey(VIEW_FACTORY_KEY, toValue: templateViewFactory) - ..bindByKey(BOUND_VIEW_FACTORY_KEY, inject: const [Injector], toFactory: (Injector injector) => - templateViewFactory.bind(injector)); - } } // TODO: This class exists for forwards API compatibility only. @@ -143,7 +133,7 @@ class ElementBinder { } // Check if there is a bind attribute for this mapping. - var bindAttr = bindAttrs["bind-${p.attrName}"]; + var bindAttr = bindAttrs[p.bindAttrName]; if (bindAttr != null) { if (p.mode == '<=>') { if (directiveScope == null) { @@ -209,15 +199,15 @@ class ElementBinder { } } - void _link(nodeInjector, probe, scope, nodeAttrs) { - _usableDirectiveRefs.forEach((DirectiveRef ref) { - var directive = nodeInjector.getByKey(ref.typeKey); - if (probe != null) { - probe.directives.add(directive); - } + void _link(DirectiveInjector directiveInjector, Scope scope, nodeAttrs) { + for(var i = 0; i < _usableDirectiveRefs.length; i++) { + DirectiveRef ref = _usableDirectiveRefs[i]; + var key = ref.typeKey; + if (identical(key, TEXT_MUSTACHE_KEY) || identical(key, ATTR_MUSTACHE_KEY)) continue; + var directive = directiveInjector.getByKey(ref.typeKey); if (ref.annotation is Controller) { - scope.context[(ref.annotation as Controller).publishAs] = directive; + scope.parentScope.context[(ref.annotation as Controller).publishAs] = directive; } var tasks = directive is AttachAware ? new _TaskList(() { @@ -244,107 +234,74 @@ class ElementBinder { if (directive is DetachAware) { scope.on(ScopeEvent.DESTROY).listen((_) => directive.detach()); } - }); + } } - void _createDirectiveFactories(DirectiveRef ref, nodeModule, node, nodesAttrsDirectives, nodeAttrs, - visibility) { - if (ref.type == TextMustache) { - nodeModule.bind(TextMustache, inject: const [Scope], - toFactory: (Scope scope) => new TextMustache(node, ref.valueAST, scope)); - } else if (ref.type == AttrMustache) { - if (nodesAttrsDirectives.isEmpty) { - nodeModule.bind(AttrMustache, inject: const[Scope], toFactory: (Scope scope) { - for (var ref in nodesAttrsDirectives) { - new AttrMustache(nodeAttrs, ref.value, ref.valueAST, scope); - } - }); - } - nodesAttrsDirectives.add(ref); + void _createDirectiveFactories(DirectiveRef ref, DirectiveInjector nodeInjector, node, + nodeAttrs) { + if (ref.typeKey == TEXT_MUSTACHE_KEY) { + new TextMustache(node, ref.valueAST, nodeInjector.scope); + } else if (ref.typeKey == ATTR_MUSTACHE_KEY) { + new AttrMustache(nodeAttrs, ref.value, ref.valueAST, nodeInjector.scope); } else if (ref.annotation is Component) { assert(ref == componentData.ref); - nodeModule.bindByKey(ref.typeKey, inject: const [Injector], - toFactory: componentData.factory.call(node), visibility: visibility); + BoundComponentFactory boundComponentFactory = componentData.factory; + Function componentFactory = boundComponentFactory.call(node); + nodeInjector.bindByKey(ref.typeKey, componentFactory, + boundComponentFactory.callArgs, ref.annotation.visibility); } else { - nodeModule.bindByKey(ref.typeKey, visibility: visibility); + nodeInjector.bindByKey(ref.typeKey, ref.factory, ref.paramKeys, ref.annotation.visibility); } } - // Overridden in TemplateElementBinder - void _registerViewFactory(node, parentInjector, nodeModule) { - nodeModule..bindByKey(VIEW_PORT_KEY, toValue: null) - ..bindByKey(VIEW_FACTORY_KEY, toValue: null) - ..bindByKey(BOUND_VIEW_FACTORY_KEY, toValue: null); - } - - - Injector bind(View view, Injector parentInjector, dom.Node node) { - Injector nodeInjector; - Scope scope = parentInjector.getByKey(SCOPE_KEY); + DirectiveInjector bind(View view, Scope scope, + DirectiveInjector parentInjector, + dom.Node node, EventHandler eventHandler, Animate animate) { var nodeAttrs = node is dom.Element ? new NodeAttrs(node) : null; - ElementProbe probe; var directiveRefs = _usableDirectiveRefs; if (!hasDirectivesOrEvents) return parentInjector; - var nodesAttrsDirectives = []; - var nodeModule = new Module() - ..bindByKey(NG_ELEMENT_KEY) - ..bindByKey(VIEW_KEY, toValue: view) - ..bindByKey(ELEMENT_KEY, toValue: node) - ..bindByKey(NODE_KEY, toValue: node) - ..bindByKey(NODE_ATTRS_KEY, toValue: nodeAttrs); - - if (_config.elementProbeEnabled) { - nodeModule.bindByKey(ELEMENT_PROBE_KEY, inject: const [], toFactory: () => probe); + DirectiveInjector nodeInjector; + if (this is TemplateElementBinder) { + nodeInjector = new TemplateDirectiveInjector(parentInjector, parentInjector.appInjector, + node, nodeAttrs, eventHandler, scope, animate, + (this as TemplateElementBinder).templateViewFactory); + } else { + nodeInjector = new DirectiveInjector(parentInjector, parentInjector.appInjector, + node, nodeAttrs, eventHandler, scope, animate); } - directiveRefs.forEach((DirectiveRef ref) { + for(var i = 0; i < directiveRefs.length; i++) { + DirectiveRef ref = directiveRefs[i]; Directive annotation = ref.annotation; - var visibility = ref.annotation.visibility; if (ref.annotation is Controller) { - scope = scope.createChild(new PrototypeMap(scope.context)); - nodeModule.bind(Scope, toValue: scope); + scope = nodeInjector.scope = scope.createChild(new PrototypeMap(scope.context)); } - - _createDirectiveFactories(ref, nodeModule, node, nodesAttrsDirectives, nodeAttrs, - visibility); - // Choose between old-style Module-based API and new-style DirectiveBinder-base API - var moduleFn = ref.annotation.module; - if (moduleFn != null) { - if (moduleFn is DirectiveBinderFn) { - var binder = new _DirectiveBinderImpl(); - moduleFn(binder); - nodeModule.install(binder.module); - } else { - nodeModule.install(moduleFn()); - } + _createDirectiveFactories(ref, nodeInjector, node, nodeAttrs); + if (ref.annotation.module != null) { + DirectiveBinderFn config = ref.annotation.module; + if (config != null) config(nodeInjector); } - }); - - _registerViewFactory(node, parentInjector, nodeModule); + if (_config.elementProbeEnabled && ref.valueAST != null) { + nodeInjector.elementProbe.bindingExpressions.add(ref.valueAST.expression); + } + } - nodeInjector = parentInjector.createChild([nodeModule]); if (_config.elementProbeEnabled) { - probe = _expando[node] = - new ElementProbe(parentInjector.getByKey(ELEMENT_PROBE_KEY), - node, nodeInjector, scope); - directiveRefs.forEach((DirectiveRef ref) { - if (ref.valueAST != null) { - probe.bindingExpressions.add(ref.valueAST.expression); - } - }); - scope.on(ScopeEvent.DESTROY).listen((_) { - _expando[node] = null; - }); + _expando[node] = nodeInjector.elementProbe; + // TODO(misko): pretty sure that clearing Expando is not necessary. Remove? + scope.on(ScopeEvent.DESTROY).listen((_) => _expando[node] = null); } - _link(nodeInjector, probe, scope, nodeAttrs); + _link(nodeInjector, scope, nodeAttrs); - onEvents.forEach((event, value) { - view.registerEvent(EventHandler.attrNameToEventName(event)); - }); + if (onEvents.isNotEmpty) { + onEvents.forEach((event, value) { + view.registerEvent(EventHandler.attrNameToEventName(event)); + }); + } return nodeInjector; } diff --git a/lib/core_dom/module_internal.dart b/lib/core_dom/module_internal.dart index 07b105844..e1fe1cbc2 100644 --- a/lib/core_dom/module_internal.dart +++ b/lib/core_dom/module_internal.dart @@ -6,17 +6,18 @@ import 'dart:html' as dom; import 'dart:js' as js; import 'package:di/di.dart'; +import 'package:di/annotations.dart'; import 'package:perf_api/perf_api.dart'; import 'package:angular/cache/module.dart'; import 'package:angular/core/annotation.dart'; -import 'package:angular/core/annotation_src.dart' - show SHADOW_DOM_INJECTOR_NAME, DirectiveBinder, DirectiveBinderFn; import 'package:angular/core/module_internal.dart'; import 'package:angular/core/parser/parser.dart'; import 'package:angular/core_dom/dom_util.dart' as util; import 'package:angular/core_dom/static_keys.dart'; +import 'package:angular/core_dom/directive_injector.dart'; +export 'package:angular/core_dom/directive_injector.dart' show DirectiveInjector; import 'package:angular/change_detection/watch_group.dart' show Watch, PrototypeMap; import 'package:angular/change_detection/ast_parser.dart'; @@ -26,8 +27,6 @@ import 'package:angular/directive/module.dart' show NgBaseCss; import 'dart:collection'; part 'animation.dart'; -part 'view.dart'; -part 'view_factory.dart'; part 'cookies.dart'; part 'common.dart'; part 'compiler.dart'; @@ -39,8 +38,8 @@ part 'element_binder_builder.dart'; part 'event_handler.dart'; part 'http.dart'; part 'mustache.dart'; +part 'ng_element.dart'; part 'node_cursor.dart'; -part 'web_platform.dart'; part 'selector.dart'; part 'shadow_dom_component_factory.dart'; part 'shadowless_shadow_root.dart'; @@ -49,8 +48,10 @@ part 'tagging_view_factory.dart'; part 'template_cache.dart'; part 'transcluding_component_factory.dart'; part 'tree_sanitizer.dart'; +part 'view.dart'; +part 'view_factory.dart'; part 'walking_compiler.dart'; -part 'ng_element.dart'; +part 'web_platform.dart'; class CoreDomModule extends Module { CoreDomModule() { @@ -62,7 +63,7 @@ class CoreDomModule extends Module { var templateCache = new TemplateCache(); register.registerCache("TemplateCache", templateCache); return templateCache; - }, inject: [CacheRegister]); + }, inject: [CACHE_REGISTER_KEY]); bind(dom.NodeTreeSanitizer, toImplementation: NullTreeSanitizer); bind(TextMustache); @@ -71,7 +72,7 @@ class CoreDomModule extends Module { bind(Compiler, toImplementation: TaggingCompiler); bind(CompilerConfig); - bind(ComponentFactory, inject: [ShadowDomComponentFactory]); + bind(ComponentFactory, inject: [SHADOW_DOM_COMPONENT_FACTORY_KEY]); bind(ShadowDomComponentFactory); bind(TranscludingComponentFactory); bind(Content); diff --git a/lib/core_dom/shadow_dom_component_factory.dart b/lib/core_dom/shadow_dom_component_factory.dart index a9b72b633..7c8a48c9a 100644 --- a/lib/core_dom/shadow_dom_component_factory.dart +++ b/lib/core_dom/shadow_dom_component_factory.dart @@ -8,7 +8,8 @@ abstract class ComponentFactory { * A Component factory with has been bound to a specific component type. */ abstract class BoundComponentFactory { - FactoryFn call(dom.Element element); + List get callArgs; + Function call(dom.Element element); static async.Future _viewFuture( Component component, ViewCache viewCache, DirectiveMap directives) { @@ -117,19 +118,20 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory { ); } - FactoryFn call(dom.Element element) { - return (Injector injector) { - Scope scope = injector.getByKey(SCOPE_KEY); - NgBaseCss baseCss = _component.useNgBaseCss ? injector.getByKey(NG_BASE_CSS_KEY) : null; - + List get callArgs => _CALL_ARGS; + static final _CALL_ARGS = [DIRECTIVE_INJECTOR_KEY, SCOPE_KEY, NG_BASE_CSS_KEY, + EVENT_HANDLER_KEY]; + Function call(dom.Element element) { + return (DirectiveInjector injector, Scope scope, NgBaseCss baseCss, + EventHandler eventHandler) { var shadowDom = element.createShadowRoot() ..applyAuthorStyles = _component.applyAuthorStyles ..resetStyleInheritance = _component.resetStyleInheritance; - var shadowScope = scope.createChild({}); // Isolate + var shadowScope = scope.createChild(new HashMap()); // Isolate async.Future> cssFuture; - if (baseCss != null) { + if (_component.useNgBaseCss == true) { cssFuture = async.Future.wait( [async.Future.wait(baseCss.urls.map(_styleFuture)), _styleElementsFuture]) .then((twoLists) { @@ -140,7 +142,7 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory { cssFuture = _styleElementsFuture; } - Injector shadowInjector; + ComponentDirectiveInjector shadowInjector; TemplateLoader templateLoader = new TemplateLoader( cssFuture.then((Iterable cssList) { @@ -153,7 +155,7 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory { return _viewFuture.then((ViewFactory viewFactory) { if (shadowScope.isAttached) { shadowDom.nodes.addAll( - viewFactory(shadowInjector).nodes); + viewFactory.call(shadowInjector.scope, shadowInjector).nodes); } return shadowDom; }); @@ -162,27 +164,13 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory { })); var probe; - var shadowModule = new Module() - ..bindByKey(_ref.typeKey) - ..bindByKey(NG_ELEMENT_KEY) - ..bindByKey(EVENT_HANDLER_KEY, toImplementation: ShadowRootEventHandler) - ..bindByKey(SCOPE_KEY, toValue: shadowScope) - ..bindByKey(TEMPLATE_LOADER_KEY, toValue: templateLoader) - ..bindByKey(SHADOW_ROOT_KEY, toValue: shadowDom); - - if (_f.config.elementProbeEnabled) { - shadowModule.bindByKey(ELEMENT_PROBE_KEY, inject: const [], toFactory: () => probe); - } - - shadowInjector = injector.createChild([shadowModule], name: SHADOW_DOM_INJECTOR_NAME); + shadowInjector = new ShadowDomComponentDirectiveInjector(injector, injector.appInjector, + shadowScope, templateLoader, shadowDom); + shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility); if (_f.config.elementProbeEnabled) { - probe = _f.expando[shadowDom] = - new ElementProbe(injector.getByKey(ELEMENT_PROBE_KEY), - shadowDom, shadowInjector, shadowScope); - shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) { - _f.expando[shadowDom] = null; - }); + probe = _f.expando[shadowDom] = shadowInjector.elementProbe; + shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _f.expando[shadowDom] = null); } var controller = shadowInjector.getByKey(_ref.typeKey); diff --git a/lib/core_dom/tagging_view_factory.dart b/lib/core_dom/tagging_view_factory.dart index d62fa1c21..c5a928ae0 100644 --- a/lib/core_dom/tagging_view_factory.dart +++ b/lib/core_dom/tagging_view_factory.dart @@ -51,46 +51,67 @@ class TaggingViewFactory implements ViewFactory { nodeLinkingInfos = computeNodeLinkingInfos(templateNodes), this.templateNodes = templateNodes; - BoundViewFactory bind(Injector injector) => new BoundViewFactory(this, injector); + @deprecated + BoundViewFactory bind(DirectiveInjector directiveInjector) => + new BoundViewFactory(this, directiveInjector); static Key _EVENT_HANDLER_KEY = new Key(EventHandler); - View call(Injector injector, [List nodes /* TODO: document fragment */]) { + View call(Scope scope, DirectiveInjector directiveInjector, + [List nodes /* TODO: document fragment */]) { + assert(scope != null); if (nodes == null) { nodes = cloneElements(templateNodes); } var timerId; try { assert((timerId = _perf.startTimer('ng.view')) != false); - var view = new View(nodes, injector.getByKey(_EVENT_HANDLER_KEY)); - _link(view, nodes, injector); + 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); return view; } finally { assert(_perf.stopTimer(timerId) != false); } } - void _bindTagged(TaggedElementBinder tagged, int elementBinderIndex, Injector rootInjector, - List elementInjectors, View view, boundNode) { + void _bindTagged(TaggedElementBinder tagged, int elementBinderIndex, + DirectiveInjector rootInjector, + List elementInjectors, View view, boundNode, Scope scope, + EventHandler eventHandler, Animate animate) { var binder = tagged.binder; - var parentInjector = tagged.parentBinderOffset == -1 ? - rootInjector : - elementInjectors[tagged.parentBinderOffset]; - assert(parentInjector != null); - - var elementInjector = elementInjectors[elementBinderIndex] = - binder != null ? binder.bind(view, parentInjector, boundNode) : parentInjector; + DirectiveInjector parentInjector = + tagged.parentBinderOffset == -1 ? rootInjector : elementInjectors[tagged.parentBinderOffset]; + + var elementInjector; + if (binder == null) { + elementInjector = parentInjector; + } else { + // TODO(misko): Remove this after we remove controllers. No controllers -> 1to1 Scope:View. + if (parentInjector != rootInjector && parentInjector.scope != null) { + scope = parentInjector.scope; + } + elementInjector = binder.bind(view, scope, parentInjector, boundNode, eventHandler, animate); + } + // TODO(misko): Remove this after we remove controllers. No controllers -> 1to1 Scope:View. + if (elementInjector != rootInjector && elementInjector.scope != null) { + scope = elementInjector.scope; + } + elementInjectors[elementBinderIndex] = elementInjector; if (tagged.textBinders != null) { for (var k = 0; k < tagged.textBinders.length; k++) { TaggedTextBinder taggedText = tagged.textBinders[k]; - taggedText.binder.bind(view, elementInjector, boundNode.childNodes[taggedText.offsetIndex]); + var childNode = boundNode.childNodes[taggedText.offsetIndex]; + taggedText.binder.bind(view, scope, elementInjector, childNode, eventHandler, animate); } } } - View _link(View view, List nodeList, Injector rootInjector) { - var elementInjectors = new List(elementBinders.length); + View _link(View view, Scope scope, List nodeList, EventHandler eventHandler, + Animate animate, DirectiveInjector rootInjector) { + var elementInjectors = new List(elementBinders.length); var directiveDefsByName = {}; var elementBinderIndex = 0; @@ -110,7 +131,8 @@ class TaggingViewFactory implements ViewFactory { if (linkingInfo.isElement) { if (linkingInfo.containsNgBinding) { var tagged = elementBinders[elementBinderIndex]; - _bindTagged(tagged, elementBinderIndex, rootInjector, elementInjectors, view, node); + _bindTagged(tagged, elementBinderIndex, rootInjector, + elementInjectors, view, node, scope, eventHandler, animate); elementBinderIndex++; } @@ -118,14 +140,16 @@ class TaggingViewFactory implements ViewFactory { var elts = (node as dom.Element).querySelectorAll('.ng-binding'); for (int j = 0; j < elts.length; j++, elementBinderIndex++) { TaggedElementBinder tagged = elementBinders[elementBinderIndex]; - _bindTagged(tagged, elementBinderIndex, rootInjector, elementInjectors, view, elts[j]); + _bindTagged(tagged, elementBinderIndex, rootInjector, elementInjectors, + view, elts[j], scope, eventHandler, animate); } } } else { TaggedElementBinder tagged = elementBinders[elementBinderIndex]; assert(tagged.binder != null || tagged.isTopLevel); if (tagged.binder != null) { - _bindTagged(tagged, elementBinderIndex, rootInjector, elementInjectors, view, node); + _bindTagged(tagged, elementBinderIndex, rootInjector, + elementInjectors, view, node, scope, eventHandler, animate); } elementBinderIndex++; } diff --git a/lib/core_dom/transcluding_component_factory.dart b/lib/core_dom/transcluding_component_factory.dart index cfc2eee70..29f8f2663 100644 --- a/lib/core_dom/transcluding_component_factory.dart +++ b/lib/core_dom/transcluding_component_factory.dart @@ -96,25 +96,24 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory { _directives); } - FactoryFn call(dom.Node node) { + List get callArgs => _CALL_ARGS; + static var _CALL_ARGS = [ DIRECTIVE_INJECTOR_KEY, SCOPE_KEY, + VIEW_CACHE_KEY, HTTP_KEY, TEMPLATE_CACHE_KEY, + DIRECTIVE_MAP_KEY, NG_BASE_CSS_KEY, EVENT_HANDLER_KEY]; + Function call(dom.Node node) { // CSS is not supported. assert(_component.cssUrls == null || _component.cssUrls.isEmpty); var element = node as dom.Element; - return (Injector injector) { + return (DirectiveInjector injector, Scope scope, + ViewCache viewCache, Http http, TemplateCache templateCache, + DirectiveMap directives, NgBaseCss baseCss, EventHandler eventHandler) { - var childInjector; + DirectiveInjector childInjector; var childInjectorCompleter; // Used if the ViewFuture is available before the childInjector. var component = _component; - Scope scope = injector.getByKey(SCOPE_KEY); - ViewCache viewCache = injector.getByKey(VIEW_CACHE_KEY); - Http http = injector.getByKey(HTTP_KEY); - TemplateCache templateCache = injector.getByKey(TEMPLATE_CACHE_KEY); - DirectiveMap directives = injector.getByKey(DIRECTIVE_MAP_KEY); - NgBaseCss baseCss = injector.getByKey(NG_BASE_CSS_KEY); - var contentPort = new ContentPort(element); // Append the component's template as children @@ -124,12 +123,14 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory { elementFuture = _viewFuture.then((ViewFactory viewFactory) { contentPort.pullNodes(); if (childInjector != null) { - element.nodes.addAll(viewFactory(childInjector).nodes); + element.nodes.addAll( + viewFactory.call(childInjector.scope, childInjector).nodes); return element; } else { childInjectorCompleter = new async.Completer(); return childInjectorCompleter.future.then((childInjector) { - element.nodes.addAll(viewFactory(childInjector).nodes); + element.nodes.addAll( + viewFactory.call(childInjector.scope, childInjector).nodes); return element; }); } @@ -139,21 +140,13 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory { } TemplateLoader templateLoader = new TemplateLoader(elementFuture); - Scope shadowScope = scope.createChild({}); + Scope shadowScope = scope.createChild(new HashMap()); - var probe; - var childModule = new Module() - ..bind(_ref.type) - ..bind(NgElement) - ..bind(ContentPort, toValue: contentPort) - ..bind(Scope, toValue: shadowScope) - ..bind(TemplateLoader, toValue: templateLoader) - ..bind(dom.ShadowRoot, toValue: new ShadowlessShadowRoot(element)); + childInjector = new ShadowlessComponentDirectiveInjector(injector, injector.appInjector, + eventHandler, shadowScope, templateLoader, new ShadowlessShadowRoot(element), + contentPort); + childInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility); - if (_f.config.elementProbeEnabled) { - childModule.bind(ElementProbe, inject: const [], toFactory: () => probe); - } - childInjector = injector.createChild([childModule], name: SHADOW_DOM_INJECTOR_NAME); if (childInjectorCompleter != null) { childInjectorCompleter.complete(childInjector); } diff --git a/lib/core_dom/view.dart b/lib/core_dom/view.dart index c53309f7d..1195fbe8c 100644 --- a/lib/core_dom/view.dart +++ b/lib/core_dom/view.dart @@ -12,10 +12,11 @@ part of angular.core.dom_internal; * */ class View { + final Scope scope; final List nodes; final EventHandler eventHandler; - View(this.nodes, this.eventHandler); + View(this.nodes, this.scope, this.eventHandler); void registerEvent(String eventName) { eventHandler.register(eventName); @@ -27,32 +28,46 @@ class View { * [placeholder] node that is used as the insertion point for view nodes. */ class ViewPort { + final DirectiveInjector directiveInjector; + final Scope scope; final dom.Node placeholder; final Animate _animate; final _views = []; - ViewPort(this.placeholder, this._animate); + ViewPort(this.directiveInjector, this.scope, this.placeholder, this._animate); - void insert(View view, { View insertAfter }) { - dom.Node previousNode = _lastNode(insertAfter); - _viewsInsertAfter(view, insertAfter); + View insertNew(ViewFactory viewFactory, { View insertAfter, Scope viewScope}) { + if (viewScope == null) viewScope = scope.createChild(new PrototypeMap(scope.context)); + View view = viewFactory.call(viewScope, directiveInjector); + return insert(view, insertAfter: insertAfter); + } - _animate.insert(view.nodes, placeholder.parentNode, - insertBefore: previousNode.nextNode); + View insert(View view, { View insertAfter }) { + scope.rootScope.domWrite(() { + dom.Node previousNode = _lastNode(insertAfter); + _viewsInsertAfter(view, insertAfter); + _animate.insert(view.nodes, placeholder.parentNode, insertBefore: previousNode.nextNode); + }); + return view; } - void remove(View view) { + View remove(View view) { + view.scope.destroy(); _views.remove(view); - _animate.remove(view.nodes); + scope.rootScope.domWrite(() { + _animate.remove(view.nodes); + }); + return view; } - void move(View view, { View moveAfter }) { + View move(View view, { View moveAfter }) { dom.Node previousNode = _lastNode(moveAfter); _views.remove(view); _viewsInsertAfter(view, moveAfter); - - _animate.move(view.nodes, placeholder.parentNode, - insertBefore: previousNode.nextNode); + scope.rootScope.domWrite(() { + _animate.move(view.nodes, placeholder.parentNode, insertBefore: previousNode.nextNode); + }); + return view; } void _viewsInsertAfter(View view, View insertAfter) { diff --git a/lib/core_dom/view_factory.dart b/lib/core_dom/view_factory.dart index 23b3d9696..2f79624f5 100644 --- a/lib/core_dom/view_factory.dart +++ b/lib/core_dom/view_factory.dart @@ -9,20 +9,21 @@ part of angular.core.dom_internal; * * The BoundViewFactory needs [Scope] to be created. */ +@deprecated class BoundViewFactory { ViewFactory viewFactory; - Injector injector; + DirectiveInjector directiveInjector; - BoundViewFactory(this.viewFactory, this.injector); + BoundViewFactory(this.viewFactory, this.directiveInjector); - View call(Scope scope) => - viewFactory(injector.createChild([new Module()..bindByKey(SCOPE_KEY, toValue: scope)])); + View call(Scope scope) => viewFactory(scope, directiveInjector); } abstract class ViewFactory implements Function { - BoundViewFactory bind(Injector injector); + @deprecated + BoundViewFactory bind(DirectiveInjector directiveInjector); - View call(Injector injector, [List elements]); + View call(Scope scope, DirectiveInjector directiveInjector, [List elements]); } /** @@ -41,24 +42,28 @@ class WalkingViewFactory implements ViewFactory { eb is ElementBinderTreeRef)); } - BoundViewFactory bind(Injector injector) => - new BoundViewFactory(this, injector); + BoundViewFactory bind(DirectiveInjector directiveInjector) => + new BoundViewFactory(this, directiveInjector); - View call(Injector injector, [List nodes]) { + View call(Scope scope, DirectiveInjector directiveInjector, [List nodes]) { + assert(directiveInjector != null); if (nodes == null) nodes = cloneElements(templateElements); var timerId; try { assert((timerId = _perf.startTimer('ng.view')) != false); - var view = new View(nodes, injector.getByKey(EVENT_HANDLER_KEY)); - _link(view, nodes, elementBinders, injector); + EventHandler eventHandler = directiveInjector.getByKey(EVENT_HANDLER_KEY); + Animate animate = directiveInjector.getByKey(ANIMATE_KEY); + var view = new View(nodes, scope, eventHandler); + _link(view, scope, nodes, elementBinders, eventHandler, animate, directiveInjector); return view; } finally { assert(_perf.stopTimer(timerId) != false); } } - View _link(View view, List nodeList, List elementBinders, - Injector parentInjector) { + View _link(View view, Scope scope, List nodeList, List elementBinders, + EventHandler eventHandler, Animate animate, + DirectiveInjector directiveInjector) { var preRenderedIndexOffset = 0; var directiveDefsByName = {}; @@ -90,17 +95,22 @@ class WalkingViewFactory implements ViewFactory { parentNode = new dom.DivElement()..append(node); } - var childInjector = binder != null ? - binder.bind(view, parentInjector, node) : - parentInjector; + DirectiveInjector childInjector; + if (binder == null) { + childInjector = directiveInjector; + } else { + childInjector = binder.bind(view, scope, directiveInjector, node, eventHandler, animate); + // TODO(misko): Remove this after we remove controllers. No controllers -> 1to1 Scope:View. + if (childInjector != directiveInjector) scope = childInjector.scope; + } if (fakeParent) { // extract the node from the parentNode. nodeList[nodeListIndex] = parentNode.nodes[0]; } if (tree.subtrees != null) { - _link(view, node.nodes, tree.subtrees, childInjector); + _link(view, scope, node.nodes, tree.subtrees, eventHandler, animate, childInjector); } } finally { assert(_perf.stopTimer(timerId) != false); @@ -190,9 +200,9 @@ String _html(obj) { class ElementProbe { final ElementProbe parent; final dom.Node element; - final Injector injector; + final DirectiveInjector injector; final Scope scope; - final directives = []; + List get directives => injector.directives; final bindingExpressions = []; final modelExpressions = []; diff --git a/lib/directive/module.dart b/lib/directive/module.dart index c5b307983..04c7a7b7c 100644 --- a/lib/directive/module.dart +++ b/lib/directive/module.dart @@ -24,6 +24,7 @@ import 'package:angular/core/annotation.dart'; import 'package:angular/core/module_internal.dart'; import 'package:angular/core/parser/parser.dart'; import 'package:angular/core_dom/module_internal.dart'; +import 'package:angular/core_dom/directive_injector.dart'; import 'package:angular/utils.dart'; import 'package:angular/change_detection/watch_group.dart'; import 'package:angular/change_detection/change_detection.dart'; @@ -63,6 +64,8 @@ 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 bind(NgBind, toValue: null); diff --git a/lib/directive/ng_base_css.dart b/lib/directive/ng_base_css.dart index 60afe0adc..74e41406d 100644 --- a/lib/directive/ng_base_css.dart +++ b/lib/directive/ng_base_css.dart @@ -8,7 +8,9 @@ part of angular.directive; * # Example *
*/ -@Decorator(selector: '[ng-base-css]') +@Decorator( + selector: '[ng-base-css]', + visibility: Visibility.CHILDREN) class NgBaseCss { List _urls = const []; diff --git a/lib/directive/ng_control.dart b/lib/directive/ng_control.dart index d49316ded..36350f73b 100644 --- a/lib/directive/ng_control.dart +++ b/lib/directive/ng_control.dart @@ -39,7 +39,7 @@ abstract class NgControl implements AttachAware, DetachAware { */ final infoStates = new Map>(); - NgControl(NgElement this._element, Injector injector, + NgControl(NgElement this._element, DirectiveInjector injector, Animate this._animate) : _parentControl = injector.parent.getByKey(NG_CONTROL_KEY); diff --git a/lib/directive/ng_form.dart b/lib/directive/ng_form.dart index c28d4c934..b54b9d501 100644 --- a/lib/directive/ng_form.dart +++ b/lib/directive/ng_form.dart @@ -19,9 +19,8 @@ part of angular.directive; module: NgForm.module, map: const { 'ng-form': '@name' }) class NgForm extends NgControl { - static final Module _module = new Module() - ..bind(NgControl, inject: const [NgForm]); - static module() => _module; + static module(DirectiveBinder binder) => + binder.bind(NgControl, inject: NG_FORM_KEY, visibility: Visibility.CHILDREN); final Scope _scope; @@ -35,7 +34,7 @@ class NgForm extends NgControl { * * [element] - The form DOM element. * * [injector] - An instance of Injector. */ - NgForm(this._scope, NgElement element, Injector injector, Animate animate) : + NgForm(this._scope, NgElement element, DirectiveInjector injector, Animate animate) : super(element, injector, animate) { if (!element.node.attributes.containsKey('action')) { diff --git a/lib/directive/ng_if.dart b/lib/directive/ng_if.dart index 562bfa181..981bf9a3b 100644 --- a/lib/directive/ng_if.dart +++ b/lib/directive/ng_if.dart @@ -4,46 +4,29 @@ part of angular.directive; * Base class for NgIf and NgUnless. */ abstract class _NgUnlessIfAttrDirectiveBase { - final BoundViewFactory _boundViewFactory; + final ViewFactory _viewFactory; final ViewPort _viewPort; final Scope _scope; View _view; - /** - * The new child scope. This child scope is recreated whenever the `ng-if` - * subtree is inserted into the DOM and destroyed when it's removed from the - * DOM. Refer - * https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-prototypical-Inheritance prototypical inheritance - */ - Scope _childScope; - - _NgUnlessIfAttrDirectiveBase(this._boundViewFactory, this._viewPort, - this._scope); + _NgUnlessIfAttrDirectiveBase(this._viewFactory, this._viewPort, this._scope) { + assert(_viewFactory != null); + } // Override in subclass. void set condition(value); void _ensureViewExists() { if (_view == null) { - _childScope = _scope.createChild(new PrototypeMap(_scope.context)); - _view = _boundViewFactory(_childScope); - var view = _view; - _scope.rootScope.domWrite(() { - _viewPort.insert(view); - }); + _view = _viewPort.insertNew(_viewFactory); } } void _ensureViewDestroyed() { if (_view != null) { - var view = _view; - _scope.rootScope.domWrite(() { - _viewPort.remove(view); - }); - _childScope.destroy(); + _viewPort.remove(_view); _view = null; - _childScope = null; } } } @@ -94,9 +77,8 @@ abstract class _NgUnlessIfAttrDirectiveBase { selector:'[ng-if]', map: const {'.': '=>condition'}) class NgIf extends _NgUnlessIfAttrDirectiveBase { - NgIf(BoundViewFactory boundViewFactory, - ViewPort viewPort, - Scope scope): super(boundViewFactory, viewPort, scope); + NgIf(ViewFactory viewFactory, ViewPort viewPort, Scope scope) + : super(viewFactory, viewPort, scope); void set condition(value) { if (toBool(value)) { @@ -156,9 +138,8 @@ class NgIf extends _NgUnlessIfAttrDirectiveBase { map: const {'.': '=>condition'}) class NgUnless extends _NgUnlessIfAttrDirectiveBase { - NgUnless(BoundViewFactory boundViewFactory, - ViewPort viewPort, - Scope scope): super(boundViewFactory, viewPort, scope); + NgUnless(ViewFactory viewFactory, ViewPort viewPort, Scope scope) + : super(viewFactory, viewPort, scope); void set condition(value) { if (!toBool(value)) { diff --git a/lib/directive/ng_include.dart b/lib/directive/ng_include.dart index be450524f..b6237366c 100644 --- a/lib/directive/ng_include.dart +++ b/lib/directive/ng_include.dart @@ -23,13 +23,15 @@ class NgInclude { final dom.Element element; final Scope scope; final ViewCache viewCache; - final Injector injector; + final Injector appInjector; + final DirectiveInjector directiveInjector; final DirectiveMap directives; View _view; Scope _scope; - NgInclude(this.element, this.scope, this.viewCache, this.injector, this.directives); + NgInclude(this.element, this.scope, this.viewCache, + this.directiveInjector, this.appInjector, this.directives); _cleanUp() { if (_view == null) return; @@ -42,11 +44,10 @@ class NgInclude { _scope = null; } - _updateContent(createView) { + _updateContent(ViewFactory viewFactory) { // create a new scope _scope = scope.createChild(new PrototypeMap(scope.context)); - _view = createView(injector.createChild([new Module() - ..bind(Scope, toValue: _scope)])); + _view = viewFactory(_scope, directiveInjector); _view.nodes.forEach((node) => element.append(node)); } diff --git a/lib/directive/ng_model.dart b/lib/directive/ng_model.dart index 1ca0b9c85..c09a47388 100644 --- a/lib/directive/ng_model.dart +++ b/lib/directive/ng_model.dart @@ -44,7 +44,7 @@ class NgModel extends NgControl implements AttachAware { Watch _watch; bool _watchCollection; - NgModel(this._scope, NgElement element, Injector injector, NodeAttrs attrs, + NgModel(this._scope, NgElement element, DirectiveInjector injector, NodeAttrs attrs, Animate animate, ElementProbe probe) : super(element, injector, animate) { @@ -477,12 +477,12 @@ class InputNumberLike { * kind would be appropriate) or, for browsers that fail to conform to the * HTML5 standard in their processing of date-like inputs. */ -@Decorator(selector: 'input[type=date][ng-model][ng-bind-type]') -@Decorator(selector: 'input[type=time][ng-model][ng-bind-type]') -@Decorator(selector: 'input[type=datetime][ng-model][ng-bind-type]') -@Decorator(selector: 'input[type=datetime-local][ng-model][ng-bind-type]') -@Decorator(selector: 'input[type=month][ng-model][ng-bind-type]') -@Decorator(selector: 'input[type=week][ng-model][ng-bind-type]') +@Decorator(selector: 'input[type=date][ng-model][ng-bind-type]', visibility: Visibility.LOCAL) +@Decorator(selector: 'input[type=time][ng-model][ng-bind-type]', visibility: Visibility.LOCAL) +@Decorator(selector: 'input[type=datetime][ng-model][ng-bind-type]', visibility: Visibility.LOCAL) +@Decorator(selector: 'input[type=datetime-local][ng-model][ng-bind-type]', visibility: Visibility.LOCAL) +@Decorator(selector: 'input[type=month][ng-model][ng-bind-type]', visibility: Visibility.LOCAL) +@Decorator(selector: 'input[type=week][ng-model][ng-bind-type]', visibility: Visibility.LOCAL) class NgBindTypeForDateLike { static const DATE = 'date'; static const NUMBER = 'number'; @@ -602,8 +602,9 @@ class NgBindTypeForDateLike { @Decorator(selector: 'input[type=week][ng-model]', module: InputDateLike.moduleFactory) class InputDateLike { - static Module moduleFactory() => new Module()..bind(NgBindTypeForDateLike, - toFactory: (dom.Element e) => new NgBindTypeForDateLike(e), inject: [dom.Element]); + static void moduleFactory(DirectiveBinder binder) + => binder.bind(NgBindTypeForDateLike, + toFactory: (dom.Element e) => new NgBindTypeForDateLike(e), inject: [ELEMENT_KEY]); final dom.InputElement inputElement; final NgModel ngModel; final NgModelOptions ngModelOptions; @@ -694,11 +695,10 @@ final _uidCounter = new _UidCounter(); * the `ng-model` property when the corresponding radio element or option is * selected. */ -@Decorator(selector: 'input[type=radio][ng-model][ng-value]') -@Decorator(selector: 'option[ng-value]') +@Decorator(selector: 'input[type=radio][ng-model][ng-value]', visibility: Visibility.LOCAL) +@Decorator(selector: 'option[ng-value]', visibility: Visibility.LOCAL) class NgValue { - static Module _module = new Module()..bind(NgValue); - static Module moduleFactory() => _module; + static module(DirectiveBinder binder) => binder.bind(NgValue, visibility: Visibility.LOCAL); final dom.Element element; var _value; @@ -780,7 +780,7 @@ class NgFalseValue { */ @Decorator( selector: 'input[type=radio][ng-model]', - module: NgValue.moduleFactory) + module: NgValue.module) class InputRadio { final dom.RadioButtonInputElement radioButtonElement; final NgModel ngModel; diff --git a/lib/directive/ng_model_select.dart b/lib/directive/ng_model_select.dart index 4070ee362..93fd87259 100644 --- a/lib/directive/ng_model_select.dart +++ b/lib/directive/ng_model_select.dart @@ -18,7 +18,8 @@ part of angular.directive; * */ @Decorator( - selector: 'select[ng-model]') + selector: 'select[ng-model]', + visibility: Visibility.CHILDREN) class InputSelect implements AttachAware { final expando = new Expando(); final dom.SelectElement _selectElement; @@ -91,7 +92,7 @@ class InputSelect implements AttachAware { * provides [ng-value] which allows binding to any expression. * */ -@Decorator(selector: 'option', module: NgValue.moduleFactory) +@Decorator(selector: 'option', module: NgValue.module) class OptionValue implements AttachAware, DetachAware { final InputSelect _inputSelectDirective; diff --git a/lib/directive/ng_repeat.dart b/lib/directive/ng_repeat.dart index b1d22e9c2..80c7cbdab 100644 --- a/lib/directive/ng_repeat.dart +++ b/lib/directive/ng_repeat.dart @@ -176,7 +176,6 @@ class NgRepeat { } else { if (changes == null) { _rows.forEach((row) { - row.scope.destroy(); _viewPort.remove(row.view); }); leftInDom.clear(); @@ -184,7 +183,6 @@ class NgRepeat { changes.forEachRemoval((CollectionChangeItem removal) { var index = removal.previousIndex; var row = _rows[index]; - row.scope.destroy(); _viewPort.remove(row.view); leftInDom.removeAt(domLength - 1 - index); }); diff --git a/lib/directive/ng_switch.dart b/lib/directive/ng_switch.dart index c9aec1275..94028231d 100644 --- a/lib/directive/ng_switch.dart +++ b/lib/directive/ng_switch.dart @@ -75,7 +75,6 @@ class NgSwitch { currentViews ..forEach((_ViewScopePair pair) { pair.port.remove(pair.view); - pair.scope.destroy(); }) ..clear(); diff --git a/lib/introspection.dart b/lib/introspection.dart index 0d5f07e2d..f130c9397 100644 --- a/lib/introspection.dart +++ b/lib/introspection.dart @@ -10,6 +10,7 @@ import 'package:di/di.dart'; import 'package:angular/animate/module.dart'; import 'package:angular/core/module_internal.dart'; import 'package:angular/core_dom/module_internal.dart'; +import 'package:angular/core_dom/directive_injector.dart' show DirectiveInjector; import 'package:angular/core/static_keys.dart'; @@ -92,7 +93,7 @@ ElementProbe ngProbe(nodeOrSelector) { * application from the browser's REPL, unit or end-to-end tests. The function * is not intended to be called from Angular application. */ -Injector ngInjector(nodeOrSelector) => ngProbe(nodeOrSelector).injector; +DirectiveInjector ngInjector(nodeOrSelector) => ngProbe(nodeOrSelector).injector; /** @@ -148,7 +149,7 @@ js.JsObject _jsProbe(ElementProbe probe) { } -js.JsObject _jsInjector(Injector injector) => +js.JsObject _jsInjector(DirectiveInjector injector) => _jsify({"get": injector.get})..['_dart_'] = injector; diff --git a/lib/introspection_js.dart b/lib/introspection_js.dart new file mode 100644 index 000000000..87ed9e7c3 --- /dev/null +++ b/lib/introspection_js.dart @@ -0,0 +1,62 @@ +library angular.introspection_expando; + +import 'dart:html' as dom; +import 'dart:js' as js; + +import 'package:di/di.dart'; +import 'package:angular/introspection.dart'; +import 'package:angular/core/module_internal.dart'; +import 'package:angular/core/static_keys.dart'; +import 'package:angular/core_dom/module_internal.dart'; + +/** + * A global write only variable which keeps track of objects attached to the + * elements. This is useful for debugging AngularDart application from the + * browser's REPL. + */ +var elementExpando = new Expando('element'); + +void publishToJavaScript() { + js.context + ..['ngProbe'] = new js.JsFunction.withThis((_, nodeOrSelector) => + _jsProbe(ngProbe(nodeOrSelector))) + ..['ngInjector'] = new js.JsFunction.withThis((_, nodeOrSelector) => + _jsInjector(ngInjector(nodeOrSelector))) + ..['ngScope'] = new js.JsFunction.withThis((_, nodeOrSelector) => + _jsScope(ngScope(nodeOrSelector), + ngProbe(nodeOrSelector).injector.getByKey(SCOPE_STATS_CONFIG_KEY))) + ..['ngQuery'] = new js.JsFunction.withThis((_, dom.Node node, String selector, + [String containsText]) => new js.JsArray.from(ngQuery(node, selector, containsText))); +} + +js.JsObject _jsProbe(ElementProbe probe) { + return new js.JsObject.jsify({ + "element": probe.element, + "injector": _jsInjector(probe.injector), + "scope": _jsScope(probe.scope, probe.injector.getByKey(SCOPE_STATS_CONFIG_KEY)), + "directives": probe.directives.map((directive) => _jsDirective(directive)) + })..['_dart_'] = probe; +} + +js.JsObject _jsInjector(injector) => + new js.JsObject.jsify({"get": injector.get})..['_dart_'] = injector; + +js.JsObject _jsScope(Scope scope, ScopeStatsConfig config) { + return new js.JsObject.jsify({ + "apply": scope.apply, + "broadcast": scope.broadcast, + "context": scope.context, + "destroy": scope.destroy, + "digest": scope.rootScope.digest, + "emit": scope.emit, + "flush": scope.rootScope.flush, + "get": (name) => scope.context[name], + "isAttached": scope.isAttached, + "isDestroyed": scope.isDestroyed, + "set": (name, value) => scope.context[name] = value, + "scopeStatsEnable": () => config.emit = true, + "scopeStatsDisable": () => config.emit = false + })..['_dart_'] = scope; +} + +_jsDirective(directive) => directive; diff --git a/lib/mock/module.dart b/lib/mock/module.dart index fbd51909c..4c0b4535f 100644 --- a/lib/mock/module.dart +++ b/lib/mock/module.dart @@ -21,6 +21,7 @@ import 'dart:js' as js; import 'package:angular/angular.dart'; import 'package:angular/core/module_internal.dart'; import 'package:angular/core_dom/module_internal.dart'; +import 'package:angular/core_dom/directive_injector.dart'; import 'package:angular/core/parser/parser.dart'; import 'package:angular/mock/static_keys.dart'; import 'package:di/di.dart'; @@ -62,7 +63,7 @@ class AngularMockModule extends Module { bind(MockHttpBackend); bind(Element, toValue: document.body); bind(Node, toValue: document.body); - bind(HttpBackend, inject: [MockHttpBackend]); + bind(HttpBackend, inject:[MOCK_HTTP_BACKEND_KEY]); bind(VmTurnZone, toFactory: () { return new VmTurnZone() ..onError = (e, s, LongStackTrace ls) => dump('EXCEPTION: $e\n$s\n$ls'); diff --git a/lib/mock/probe.dart b/lib/mock/probe.dart index 09bc60a81..681549b84 100644 --- a/lib/mock/probe.dart +++ b/lib/mock/probe.dart @@ -10,9 +10,10 @@ part of angular.mock; * rootScope.myProbe.directive(SomeAttrDirective); */ @Decorator(selector: '[probe]') +@deprecated class Probe implements DetachAware { final Scope scope; - final Injector injector; + final DirectiveInjector injector; final Element element; String _probeName; diff --git a/lib/mock/test_bed.dart b/lib/mock/test_bed.dart index 54578868b..d1c54da81 100644 --- a/lib/mock/test_bed.dart +++ b/lib/mock/test_bed.dart @@ -8,6 +8,7 @@ part of angular.mock; */ class TestBed { final Injector injector; + final DirectiveInjector directiveInjector; final Scope rootScope; final Compiler compiler; final Parser _parser; @@ -17,7 +18,7 @@ class TestBed { List rootElements; View rootView; - TestBed(this.injector, this.rootScope, this.compiler, this._parser, this.expando); + TestBed(this.injector, this.directiveInjector, this.rootScope, this.compiler, this._parser, this.expando); /** @@ -35,10 +36,7 @@ class TestBed { * An option [scope] parameter can be supplied to link it with non root scope. */ Element compile(html, {Scope scope, DirectiveMap directives}) { - var injector = this.injector; - if (scope != null) { - injector = injector.createChild([new Module()..bind(Scope, toValue: scope)]); - } + if (scope == null) scope = rootScope; if (html is String) { rootElements = toNodeList(html); } else if (html is Node) { @@ -52,7 +50,7 @@ class TestBed { if (directives == null) { directives = injector.getByKey(DIRECTIVE_MAP_KEY); } - rootView = compiler(rootElements, directives)(injector, rootElements); + rootView = compiler(rootElements, directives)(scope, injector.get(DirectiveInjector), rootElements); return rootElement; } diff --git a/lib/mock/test_injection.dart b/lib/mock/test_injection.dart index e05e33f11..79aea419b 100644 --- a/lib/mock/test_injection.dart +++ b/lib/mock/test_injection.dart @@ -3,21 +3,21 @@ library angular.mock.test_injection; import 'package:angular/application_factory.dart'; import 'package:angular/mock/module.dart'; import 'package:di/di.dart'; -import 'package:di/dynamic_injector.dart'; +import 'dart:mirrors'; _SpecInjector _currentSpecInjector = null; class _SpecInjector { - DynamicInjector moduleInjector; - DynamicInjector injector; + Injector moduleInjector; + Injector injector; dynamic injectiorCreateLocation; final modules = []; final initFns = []; _SpecInjector() { var moduleModule = new Module() - ..bind(Module, toFactory: () => addModule(new Module()), inject: []); - moduleInjector = new DynamicInjector(modules: [moduleModule]); + ..bind(Module, toFactory: () => addModule(new Module())); + moduleInjector = new ModuleInjector([moduleModule]); } addModule(module) { @@ -34,7 +34,7 @@ class _SpecInjector { } try { if (fnOrModule is Function) { - var initFn = moduleInjector.invoke(fnOrModule); + var initFn = _invoke(moduleInjector, fnOrModule); if (initFn is Function) initFns.add(initFn); } else if (fnOrModule is Module) { addModule(fnOrModule); @@ -50,12 +50,12 @@ class _SpecInjector { try { if (injector == null) { injectiorCreateLocation = declarationStack; - injector = new DynamicInjector(modules: modules); // Implicit injection is disabled. + injector = new ModuleInjector(modules); // Implicit injection is disabled. initFns.forEach((fn) { - injector.invoke(fn); + _invoke(injector, fn); }); } - injector.invoke(fn); + _invoke(injector, fn); } catch (e, s) { throw "$e\n$s\nDECLARED AT:$declarationStack"; } @@ -65,6 +65,20 @@ class _SpecInjector { injector = null; injectiorCreateLocation = null; } + + _invoke(Injector injector, Function fn) { + ClosureMirror cm = reflect(fn); + MethodMirror mm = cm.function; + List args = mm.parameters.map((ParameterMirror parameter) { + var metadata = parameter.metadata; + Key key = new Key( + (parameter.type as ClassMirror).reflectedType, + metadata.isEmpty ? null : metadata.first.type.reflectedType); + return injector.getByKey(key); + }).toList(); + + return cm.apply(args).reflectee; + } } /** diff --git a/lib/perf/module.dart b/lib/perf/module.dart index 01de8a563..7ee7b4072 100644 --- a/lib/perf/module.dart +++ b/lib/perf/module.dart @@ -22,6 +22,6 @@ part 'dev_tools_timeline.dart'; class PerfModule extends Module { PerfModule() { - bind(Profiler, toImplementation: Profiler); + bind(Profiler, toFactory: () => new Profiler(), inject: []); } } diff --git a/lib/playback/playback_http.dart b/lib/playback/playback_http.dart index 7efe1d2f6..2522a3605 100644 --- a/lib/playback/playback_http.dart +++ b/lib/playback/playback_http.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:convert' show JSON; import 'dart:html'; +import 'package:di/annotations.dart'; import 'package:angular/core_dom/module_internal.dart'; import 'package:angular/core/annotation_src.dart'; import 'package:angular/mock/http_backend.dart' as mock; diff --git a/lib/routing/module.dart b/lib/routing/module.dart index 9e479dab6..7430de4cd 100644 --- a/lib/routing/module.dart +++ b/lib/routing/module.dart @@ -126,6 +126,7 @@ import 'dart:async'; import 'dart:html'; import 'package:di/di.dart'; +import 'package:di/annotations.dart'; import 'package:angular/application.dart'; import 'package:angular/core/annotation_src.dart'; import 'package:angular/core/module_internal.dart'; diff --git a/lib/routing/ng_bind_route.dart b/lib/routing/ng_bind_route.dart index cf77eff16..52c786626 100644 --- a/lib/routing/ng_bind_route.dart +++ b/lib/routing/ng_bind_route.dart @@ -29,13 +29,10 @@ part of angular.routing; class NgBindRoute implements RouteProvider { String routeName; final Router _router; - final Injector _injector; + final DirectiveInjector _injector; - static final Module _module = new Module() - ..bind(RouteProvider, inject: const [NgBindRoute], - visibility: Directive.CHILDREN_VISIBILITY); - - static Module module() => _module; + static void module(DirectiveBinder binder) + => binder.bind(RouteProvider, inject: NG_BIND_ROUTE_KEY, visibility: Visibility.CHILDREN); // We inject NgRoutingHelper to force initialization of routing. NgBindRoute(this._router, this._injector, NgRoutingHelper _); diff --git a/lib/routing/ng_view.dart b/lib/routing/ng_view.dart index ca54c58d7..cf1aaa2c9 100644 --- a/lib/routing/ng_view.dart +++ b/lib/routing/ng_view.dart @@ -53,16 +53,16 @@ part of angular.routing; */ @Decorator( selector: 'ng-view', - module: NgView.module) + module: NgView.module, + visibility: Visibility.CHILDREN) class NgView implements DetachAware, RouteProvider { - static final Module _module = new Module() - ..bind(RouteProvider, inject: const [NgView]); - - static Module module() => _module; + static void module(DirectiveBinder binder) + => binder.bind(RouteProvider, inject: NG_VIEW_KEY, visibility: Visibility.CHILDREN); final NgRoutingHelper _locationService; final ViewCache _viewCache; - final Injector _injector; + final Injector _appInjector; + final DirectiveInjector _dirInjector; final Element _element; final Scope _scope; RouteHandle _route; @@ -71,11 +71,12 @@ class NgView implements DetachAware, RouteProvider { Scope _childScope; Route _viewRoute; - NgView(this._element, this._viewCache, Injector injector, Router router, this._scope) - : _injector = injector, - _locationService = injector.getByKey(NG_ROUTING_HELPER_KEY) + NgView(this._element, this._viewCache, DirectiveInjector dirInjector, this._appInjector, + Router router, this._scope) + : _dirInjector = dirInjector, + _locationService = dirInjector.getByKey(NG_ROUTING_HELPER_KEY) { - RouteProvider routeProvider = injector.parent.getByKey(NG_VIEW_KEY); + RouteProvider routeProvider = dirInjector.parent.getByKey(NG_VIEW_KEY); _route = routeProvider != null ? routeProvider.route.newHandle() : router.root.newHandle(); @@ -107,19 +108,22 @@ class NgView implements DetachAware, RouteProvider { _cleanUp(); }); - var viewInjector = modules == null ? - _injector : - forceNewDirectivesAndFormatters(_injector, modules); + Injector viewInjector = _appInjector; + DirectiveInjector directiveInjector = _dirInjector; + + if (modules != null) { + viewInjector = forceNewDirectivesAndFormatters(_appInjector, _dirInjector, modules); + directiveInjector = viewInjector.get(DirectiveInjector); + } var newDirectives = viewInjector.getByKey(DIRECTIVE_MAP_KEY); var viewFuture = viewDef.templateHtml != null ? new Future.value(_viewCache.fromHtml(viewDef.templateHtml, newDirectives)) : _viewCache.fromUrl(viewDef.template, newDirectives); - viewFuture.then((viewFactory) { + viewFuture.then((ViewFactory viewFactory) { _cleanUp(); _childScope = _scope.createChild(new PrototypeMap(_scope.context)); - _view = viewFactory( - viewInjector.createChild([new Module()..bind(Scope, toValue: _childScope)])); + _view = viewFactory(_childScope, directiveInjector); _view.nodes.forEach((elm) => _element.append(elm)); }); } diff --git a/lib/tools/expression_extractor.dart b/lib/tools/expression_extractor.dart index 027e610bf..0a10b9ac6 100644 --- a/lib/tools/expression_extractor.dart +++ b/lib/tools/expression_extractor.dart @@ -10,7 +10,6 @@ import 'package:angular/tools/io_impl.dart'; import 'package:angular/tools/common.dart'; import 'package:di/di.dart'; -import 'package:di/dynamic_injector.dart'; import 'package:angular/core/parser/parser.dart'; import 'package:angular/core/parser/lexer.dart'; @@ -61,7 +60,7 @@ main(args) { ..bind(CacheRegister) ..bind(Parser, inject: [DynamicParser]) ..bind(ParserBackend, inject: [DartGetterSetterGen]); - Injector injector = new DynamicInjector(modules: [module]); + Injector injector = new ModuleInjector([module]); runZoned(() { // Run the generator. diff --git a/lib/tools/transformer/expression_generator.dart b/lib/tools/transformer/expression_generator.dart index 020f8c84c..4bd1fdb74 100644 --- a/lib/tools/transformer/expression_generator.dart +++ b/lib/tools/transformer/expression_generator.dart @@ -2,7 +2,9 @@ library angular.tools.transformer.expression_generator; import 'dart:async'; import 'package:analyzer/src/generated/element.dart'; +import 'package:angular/cache/module.dart'; import 'package:angular/core/parser/parser.dart'; +import 'package:angular/core/parser/lexer.dart'; import 'package:angular/tools/html_extractor.dart'; import 'package:angular/tools/parser_getter_setter/generator.dart'; import 'package:angular/tools/source_crawler.dart'; @@ -12,7 +14,7 @@ import 'package:angular/tools/transformer/referenced_uris.dart'; import 'package:barback/barback.dart'; import 'package:code_transformers/resolver.dart'; import 'package:di/di.dart'; -import 'package:di/dynamic_injector.dart'; +import 'package:di/src/reflector_dynamic.dart'; import 'package:path/path.dart' as path; /** @@ -45,11 +47,13 @@ class ExpressionGenerator extends Transformer with ResolverTransformer { return _getHtmlSources(transform, resolver) .forEach(htmlExtractor.parseHtml) .then((_) { - var module = new Module() - ..bind(Parser, inject: const [DynamicParser]) - ..bind(ParserBackend, inject: const [DartGetterSetterGen]); - var injector = - new DynamicInjector(modules: [module], allowImplicitInjection: true); + var module = new Module.withReflector(getReflector()) + ..install(new CacheModule.withReflector(getReflector())) + ..bind(Parser, toImplementation: DynamicParser) + ..bind(ParserBackend, toImplementation: DartGetterSetterGen) + ..bind(Lexer) + ..bind(_ParserGetterSetter); + var injector = new ModuleInjector([module]); injector.get(_ParserGetterSetter).generateParser( htmlExtractor.expressions.toList(), outputBuffer); diff --git a/lib/tools/transformer/options.dart b/lib/tools/transformer/options.dart index 3a730075a..fc6560a17 100644 --- a/lib/tools/transformer/options.dart +++ b/lib/tools/transformer/options.dart @@ -1,6 +1,6 @@ library angular.tools.transformer.options; -import 'package:di/transformer/options.dart' as di; +import 'package:di/transformer.dart' as di show TransformOptions; /** Options used by Angular transformers */ class TransformOptions { diff --git a/lib/tools/transformer/static_angular_generator.dart b/lib/tools/transformer/static_angular_generator.dart index 284b9924f..69f130eea 100644 --- a/lib/tools/transformer/static_angular_generator.dart +++ b/lib/tools/transformer/static_angular_generator.dart @@ -51,9 +51,6 @@ class StaticAngularGenerator extends Transformer with ResolverTransformer { _addImport(transaction, unit, '${generatedFilePrefix}_static_metadata.dart', 'generated_static_metadata'); - _addImport(transaction, unit, - '${generatedFilePrefix}_static_injector.dart', - 'generated_static_injector'); var printer = transaction.commit(); var url = id.path.startsWith('lib/') @@ -82,7 +79,6 @@ class _NgDynamicToStaticVisitor extends GeneralizingAstVisitor { var args = m.argumentList; transaction.edit(args.beginToken.offset + 1, args.end - 1, - 'generated_static_injector.factories, ' 'generated_static_metadata.typeAnnotations, ' 'generated_static_expressions.getters, ' 'generated_static_expressions.setters, ' diff --git a/lib/transformer.dart b/lib/transformer.dart index c884a6c16..5c68cbb91 100644 --- a/lib/transformer.dart +++ b/lib/transformer.dart @@ -9,8 +9,7 @@ import 'package:angular/tools/transformer/html_dart_references_generator.dart'; import 'package:angular/tools/transformer/options.dart'; import 'package:barback/barback.dart'; import 'package:code_transformers/resolver.dart'; -import 'package:di/transformer/injector_generator.dart' show InjectorGenerator; -import 'package:di/transformer/options.dart' as di; +import 'package:di/transformer.dart' as di; import 'package:path/path.dart' as path; @@ -34,7 +33,7 @@ class AngularTransformerGroup implements TransformerGroup { TransformOptions _parseSettings(Map args) { // Default angular annotations for injectable types var annotations = [ - 'angular.core.annotation_src.Injectable', + 'di.annotations.Injectable', 'angular.core.annotation_src.Decorator', 'angular.core.annotation_src.Controller', 'angular.core.annotation_src.Component', @@ -120,10 +119,10 @@ Map _readStringMapValue(Map args, String name) { List> _createPhases(TransformOptions options) { var resolvers = new Resolvers(options.sdkDirectory); return [ - [new HtmlDartReferencesGenerator(options)], - [new _SerialTransformer([ + [ new HtmlDartReferencesGenerator(options) ], + [ new di.InjectorGenerator(options.diOptions, resolvers) ], + [ new _SerialTransformer([ new ExpressionGenerator(options, resolvers), - new InjectorGenerator(options.diOptions, resolvers), new MetadataGenerator(options, resolvers), new StaticAngularGenerator(options, resolvers) ])] diff --git a/pubspec.lock b/pubspec.lock index 4a9e203cc..55c24e6b6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -4,7 +4,7 @@ packages: analyzer: description: analyzer source: hosted - version: "0.13.6" + version: "0.15.7" args: description: args source: hosted @@ -24,15 +24,17 @@ packages: code_transformers: description: code_transformers source: hosted - version: "0.1.3+1" + version: "0.1.4+2" collection: description: collection source: hosted version: "0.9.2" di: - description: di - source: hosted - version: "1.2.3" + description: + path: "../di.dart" + relative: true + source: path + version: "2.0.0-alpha.10" guinness: description: guinness source: hosted @@ -44,7 +46,7 @@ packages: intl: description: intl source: hosted - version: "0.9.9" + version: "0.9.10" js: description: js source: hosted diff --git a/pubspec.yaml b/pubspec.yaml index d32395bb9..0a78a5000 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,12 +14,12 @@ environment: sdk: '>=1.4.0' dependencies: args: '>=0.10.0 < 0.11.0' - analyzer: '>=0.13.0 <0.14.0' - barback: '>=0.11.1 <0.14.0' + analyzer: '>=0.15.0 <0.19.0' + barback: '>=0.13.0 <0.17.0' browser: '>=0.10.0 <0.11.0' - code_transformers: '>=0.1.3 <0.2.0' + code_transformers: '>=0.1.4+2 <0.2.0' collection: '>=0.9.1 <1.0.0' - di: '>=1.2.3 <2.0.0' + di: '>=2.0.0-alpha.10' html5lib: '>=0.10.0 <0.11.0' intl: '>=0.8.7 <0.10.0' perf_api: '>=0.0.8 <0.1.0' diff --git a/test/_specs.dart b/test/_specs.dart index d0c81243d..f6c024973 100644 --- a/test/_specs.dart +++ b/test/_specs.dart @@ -14,7 +14,6 @@ export 'package:guinness/guinness_html.dart'; export 'package:mock/mock.dart'; export 'package:di/di.dart'; -export 'package:di/dynamic_injector.dart'; export 'package:angular/angular.dart'; export 'package:angular/application.dart'; export 'package:angular/introspection.dart'; diff --git a/test/angular_spec.dart b/test/angular_spec.dart index b601b08da..a139a9c0f 100644 --- a/test/angular_spec.dart +++ b/test/angular_spec.dart @@ -107,12 +107,12 @@ main() { "angular.core.annotation_src.DirectiveBinder", "angular.core.annotation_src.DirectiveBinderFn", "angular.core.annotation_src.Formatter", - "angular.core.annotation_src.Injectable", "angular.core.annotation_src.NgAttr", "angular.core.annotation_src.NgCallback", "angular.core.annotation_src.NgOneWay", "angular.core.annotation_src.NgOneWayOneTime", "angular.core.annotation_src.NgTwoWay", + "angular.core.annotation_src.Visibility", "angular.core.dom_internal.Animate", "angular.core.dom_internal.Animation", "angular.core.dom_internal.AnimationResult", @@ -233,6 +233,7 @@ main() { "angular.introspection.ngProbe", "angular.introspection.ngQuery", "angular.introspection.ngScope", + "angular.node_injector.DirectiveInjector", "angular.routing.NgBindRoute", "angular.routing.ngRoute", "angular.routing.NgRouteCfg", @@ -249,16 +250,21 @@ main() { "angular.watch_group.Watch", "change_detection.AvgStopwatch", "change_detection.FieldGetterFactory", - "di.DEFAULT_VALUE", - "di.CircularDependencyError", - "di.FactoryFn", - "di.injector.Injector", - "di.InvalidBindingError", "di.key.Key", - "di.Module", - "di.NoProviderError", - "di.TypeFactory", - "di.Visibility", + "di.key.key", + "di.injector.Injector", + "di.injector.ModuleInjector", + "di.module.Module", + "di.module.Binding", + "di.module.DEFAULT_VALUE", + "di.reflector.TypeReflector", + "di.errors.ResolvingError", + "di.errors.CircularDependencyError", + "di.errors.NoProviderError", + "di.errors.DynamicReflectorError", + "di.errors.NoGeneratedTypeFactoryError", + "di.annotations.Injectable", + "di.annotations.Injectables", "route.client.Routable", "route.client.Route", "route.client.RouteEnterEvent", @@ -266,10 +272,10 @@ main() { "route.client.RouteEvent", "route.client.RouteHandle", "route.client.RouteImpl", - "route.client.RoutePreLeaveEvent", - "route.client.RoutePreLeaveEventHandler", "route.client.RouteLeaveEvent", "route.client.RouteLeaveEventHandler", + "route.client.RoutePreLeaveEvent", + "route.client.RoutePreLeaveEventHandler", "route.client.RoutePreEnterEvent", "route.client.RoutePreEnterEventHandler", "route.client.Router", diff --git a/test/core/annotation_src_spec.dart b/test/core/annotation_src_spec.dart index 2bfeafce3..dec1b2c9b 100644 --- a/test/core/annotation_src_spec.dart +++ b/test/core/annotation_src_spec.dart @@ -40,7 +40,7 @@ void main() => describe('annotations', () { applyAuthorStyles: true, resetStyleInheritance: true, publishAs: '', - module: (){}, + module: (i){}, map: {}, selector: '', visibility: Directive.LOCAL_VISIBILITY, @@ -63,7 +63,7 @@ void main() => describe('annotations', () { children: 'xxx', map: {}, selector: '', - module: (){}, + module: (i){}, visibility: Directive.LOCAL_VISIBILITY, exportExpressions: [], exportExpressionAttrs: [] @@ -84,7 +84,7 @@ void main() => describe('annotations', () { children: 'xxx', map: {}, selector: '', - module: (){}, + module: (i){}, visibility: Directive.LOCAL_VISIBILITY, exportExpressions: [], exportExpressionAttrs: [] diff --git a/test/core/templateurl_spec.dart b/test/core/templateurl_spec.dart index 693127947..a1d6c283d 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)(injector, [element]); + compile([element], directives)(rootScope, injector.get(DirectiveInjector), [element]); }); backend.flush(); @@ -94,7 +94,7 @@ void main() { backend.expectGET('simple.html').respond(200, '
Simple!
'); var element = es('
ignore
'); - compile(element, directives)(injector, element); + compile(element, directives)(rootScope, injector.get(DirectiveInjector), element); microLeap(); backend.flush(); @@ -116,7 +116,7 @@ void main() { 'ignore' 'ignore' '
'); - compile(element, directives)(injector, element); + compile(element, directives)(rootScope, injector.get(DirectiveInjector), element); microLeap(); backend.flush(); @@ -137,7 +137,7 @@ void main() { ..expectGET('simple.html').respond(200, '
Simple!
'); var element = e('
ignore
'); - compile([element], directives)(injector, [element]); + compile([element], directives)(rootScope, injector.get(DirectiveInjector), [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)(injector, element); + compile(element, directives)(rootScope, injector.get(DirectiveInjector), 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)(injector, element); + compile(element, directives)(rootScope, injector.get(DirectiveInjector), 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)(injector, element); + compile(element, directives)(rootScope, injector.get(DirectiveInjector), element); microLeap(); backend.flush(); @@ -203,7 +203,7 @@ void main() { ..expectGET('simple.html').respond(200, '
Simple!
'); var element = es('ignore'); - compile(element, directives)(injector, element); + compile(element, directives)(rootScope, injector.get(DirectiveInjector), element); microLeap(); backend.flush(); @@ -228,7 +228,7 @@ void main() { ..expectGET('simple.html').respond(200, '
Simple!
'); var element = e('
ignore
'); - compile([element], directives)(injector, [element]); + compile([element], directives)(rootScope, injector.get(DirectiveInjector), [element]); microLeap(); backend.flush(); @@ -252,14 +252,14 @@ void main() { }); it('should load css from the style cache for the second component', async(inject( - (Http http, Compiler compile, MockHttpBackend backend, + (Http http, Compiler compile, MockHttpBackend backend, RootScope rootScope, DirectiveMap directives, Injector injector) { backend ..expectGET('simple.css').respond(200, '.hello{}') ..expectGET('simple.html').respond(200, '
Simple!
'); var element = e('
ignore
'); - compile([element], directives)(injector, [element]); + compile([element], directives)(rootScope, injector.get(DirectiveInjector), [element]); microLeap(); backend.flush(); @@ -270,7 +270,7 @@ void main() { ); var element2 = e('
ignore
'); - compile([element2], directives)(injector, [element2]); + compile([element2], directives)(rootScope, injector.get(DirectiveInjector), [element2]); microLeap(); diff --git a/test/core_dom/compiler_spec.dart b/test/core_dom/compiler_spec.dart index 0345a9660..abf996847 100644 --- a/test/core_dom/compiler_spec.dart +++ b/test/core_dom/compiler_spec.dart @@ -1,6 +1,7 @@ library compiler_spec; import '../_specs.dart'; +import 'package:angular/core_dom/directive_injector.dart'; forBothCompilers(fn) { @@ -86,6 +87,7 @@ void main() { ..bind(MyController) ..bind(MyParentController) ..bind(MyChildController) + ..bind(MyScopeModifyingController) ..bind(SameNameDecorator) ..bind(SameNameTransclude); }); @@ -376,7 +378,7 @@ void main() { var simpleElement = _.rootElement.querySelector('simple'); expect(simpleElement).toHaveText('INNER(innerText)'); var simpleProbe = ngProbe(simpleElement); - var simpleComponent = simpleProbe.injector.get(SimpleComponent); + var simpleComponent = simpleProbe.injector.getByKey(new Key(SimpleComponent)); expect(simpleComponent.scope.context['name']).toEqual('INNER'); var shadowRoot = simpleElement.shadowRoot; @@ -631,7 +633,7 @@ void main() { it('should expose PublishModuleDirectiveSuperType as PublishModuleDirectiveSuperType', () { _.compile(r'
'); - var probe = _.rootScope.context['publishModuleProbe']; + Probe probe = _.rootScope.context['publishModuleProbe']; var directive = probe.injector.get(PublishModuleDirectiveSuperType); expect(directive is PublishModuleAttrDirective).toBeTruthy(); }); @@ -660,7 +662,7 @@ void main() { scope.context['logger'] = logger; scope.context['once'] = null; var elts = es('{{logger("inner")}}'); - compile(elts, _.injector.get(DirectiveMap))(_.injector.createChild([new Module()..bind(Scope, toValue: scope)]), elts); + compile(elts, _.injector.get(DirectiveMap))(scope, _.directiveInjector, elts); expect(logger).toEqual(['new']); expect(logger).toEqual(['new']); @@ -692,7 +694,7 @@ void main() { backend.whenGET('foo.html').respond('
WORKED
'); var elts = es(''); var scope = _.rootScope.createChild({}); - compile(elts, _.injector.get(DirectiveMap))(_.injector.createChild([new Module()..bind(Scope, toValue: scope)]), elts); + compile(elts, _.injector.get(DirectiveMap))(scope, _.directiveInjector, elts); expect(logger).toEqual(['SimpleAttachComponent']); scope.destroy(); @@ -724,8 +726,9 @@ void main() { describe('invalid components', () { it('should throw a useful error message for missing selectors', () { Module module = new Module() + ..bind(DirectiveMap) ..bind(MissingSelector); - var injector = _.injector.createChild([module], forceNewInstances: [Compiler, DirectiveMap]); + var injector = _.injector.createChild([module]); var c = injector.get(Compiler); var directives = injector.get(DirectiveMap); expect(() { @@ -736,8 +739,9 @@ void main() { it('should throw a useful error message for invalid selector', () { Module module = new Module() + ..bind(DirectiveMap) ..bind(InvalidSelector); - var injector = _.injector.createChild([module], forceNewInstances: [Compiler, DirectiveMap]); + var injector = _.injector.createChild([module]); var c = injector.get(Compiler); var directives = injector.get(DirectiveMap); @@ -878,7 +882,24 @@ void main() { expect(log.result()).toEqual('Ignore; TabComponent-0; LocalAttrDirective-0; PaneComponent-1; LocalAttrDirective-0'); })); - it('should reuse controllers for transclusions', async((Logger log) { + /* + This test is dissabled becouse I (misko) thinks it has no real use case. It is easier + to understand in terms of ng-repeat + + + + + + + Should pane be allowed to get a hold of ng-repeat? Right now ng-repeat injector is + to the side and is not in any of the parents of the pane. Making an injector a + parent of pane would put the ng-repeat between tabs and pane and it would break + the DirectChild between tabs and pane. + + It is not clear to me (misko) that there is a use case for getting hold of the + tranrscluding directive such a ng-repeat. + */ + xit('should reuse controllers for transclusions', async((Logger log) { _.compile('
view
'); microLeap(); @@ -896,6 +917,16 @@ void main() { expect(element.text).toContain('my data'); }); + it('should pass the right scope into inner mustache', (TestBed _) { + var element = _.compile('
' + '
{{ data }}
' + '
'); + + _.rootScope.apply(); + + expect(element.text).toContain('my data'); + }); + it('should expose a ancestor controller to the scope of its children thru a undecorated element', (TestBed _) { var element = _.compile( '
' @@ -917,6 +948,7 @@ void main() { _.compile('
{{name}}
'); _.rootScope.apply(); expect(_.rootScope.context['name']).toEqual('cover me'); + expect(_.rootScope.context['myCtrl'] is MyController).toEqual(true); expect(_.rootElement.text).toEqual('MyController'); }); @@ -933,6 +965,13 @@ void main() { })); } +@Controller( + selector: '[my-scope-modifying-controller]') +class MyScopeModifyingController { + MyScopeModifyingController(Scope s) { + s.context['data'] = 'my data'; + } +} @Controller( selector: '[my-parent-controller]', @@ -986,7 +1025,8 @@ class LocalAttrDirective { @Decorator( selector: '[simple-transclude-in-attach]', - visibility: Directive.CHILDREN_VISIBILITY, children: Directive.TRANSCLUDE_CHILDREN) + visibility: Visibility.LOCAL, + children: Directive.TRANSCLUDE_CHILDREN) class SimpleTranscludeInAttachAttrDirective { SimpleTranscludeInAttachAttrDirective(ViewPort viewPort, BoundViewFactory boundViewFactory, Logger log, RootScope scope) { scope.runAsync(() { @@ -1035,12 +1075,10 @@ class PublishModuleDirectiveSuperType { selector: '[publish-types]', module: PublishModuleAttrDirective.module) class PublishModuleAttrDirective implements PublishModuleDirectiveSuperType { - static Module _module = new Module() - ..bind(PublishModuleDirectiveSuperType, toFactory: (i) => i.get(PublishModuleAttrDirective)); - static module() => _module; + static module(i) => i.bind(PublishModuleDirectiveSuperType, inject: PublishModuleAttrDirective); - static Injector _injector; - PublishModuleAttrDirective(Injector injector) { + static DirectiveInjector _injector; + PublishModuleAttrDirective(DirectiveInjector injector) { _injector = injector; } } @@ -1348,8 +1386,8 @@ class OneTimeDecorator { ) class SameNameTransclude { var valueTransclude; - SameNameTransclude(ViewPort port, ViewFactory factory, RootScope scope, Injector injector) { - port.insert(factory.call(injector)); + SameNameTransclude(ViewPort port, ViewFactory factory, RootScope scope) { + port.insertNew(factory); scope.context['sameTransclude'] = this; } } diff --git a/test/core_dom/directive_injector_spec.dart b/test/core_dom/directive_injector_spec.dart new file mode 100644 index 000000000..59d0aadbb --- /dev/null +++ b/test/core_dom/directive_injector_spec.dart @@ -0,0 +1,122 @@ +library directive_injector_spec; + +import '../_specs.dart'; +import 'package:angular/core_dom/directive_injector.dart'; + +void main() { + describe('DirectiveInjector', () { + + var appInjector = new ModuleInjector([new Module()..bind(_Root)]); + var div = new DivElement(); + var span = new SpanElement(); + var eventHandler = new EventHandler(null, null, null); + + describe('base', () { + DirectiveInjector injector; + Scope scope; + Animate animate; + + addDirective(Type type, [Visibility visibility]) { + if (visibility == null) visibility = Visibility.LOCAL; + var reflector = Module.DEFAULT_REFLECTOR; + injector.bindByKey( + new Key(type), + reflector.factoryFor(type), + reflector.parameterKeysFor(type), + visibility); + } + + beforeEach((Scope _scope, Animate _animate) { + scope = _scope; + animate = _animate; + injector = new DirectiveInjector(null, appInjector, div, new NodeAttrs(div), eventHandler, scope, animate); + }); + + it('should return basic types', () { + expect(injector.parent is DefaultDirectiveInjector).toBe(true); + 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); + expect((injector.get(NodeAttrs) as NodeAttrs).element).toBe(div); + expect(injector.get(EventHandler)).toBe(eventHandler); + expect(injector.get(Animate)).toBe(animate); + expect((injector.get(ElementProbe) as ElementProbe).element).toBe(div); + }); + + it('should instantiate types', () { + addDirective(_Type9); + addDirective(_Type8); + addDirective(_Type7); + addDirective(_Type5); + addDirective(_Type6); + addDirective(_Type0); + addDirective(_Type1); + addDirective(_Type2); + addDirective(_Type3); + addDirective(_Type4); + expect(() => addDirective(_TypeA)) + .toThrow('Maximum number of directives per element reached.'); + var root = injector.get(_Root); + expect((injector.get(_Type9) as _Type9).type8.type7.type6.type5.type4.type3.type2.type1.type0.root) + .toBe(root); + expect(() => injector.get(_TypeA)).toThrow('No provider found for _TypeA'); + }); + + describe('Visibility', () { + DirectiveInjector childInjector; + DirectiveInjector leafInjector; + + beforeEach(() { + childInjector = new DirectiveInjector(injector, appInjector, span, null, null, null, null); + leafInjector = new DirectiveInjector(childInjector, appInjector, span, null, null, null, null); + }); + + it('should not allow reseting visibility', () { + addDirective(_Type0, Visibility.LOCAL); + expect(() => addDirective(_Type0, Visibility.DIRECT_CHILD)).toThrow( + 'Can not set Visibility: DIRECT_CHILD on _Type0, it alread has Visibility: LOCAL'); + }); + + it('should allow child injector to see types declared at parent injector', () { + addDirective(_Children, Visibility.CHILDREN); + _Children t = injector.get(_Children); + expect(childInjector.get(_Children)).toBe(t); + expect(leafInjector.get(_Children)).toBe(t); + }); + + it('should hide parent injector types when local visibility', () { + addDirective(_Local, Visibility.LOCAL); + _Local t = injector.getByKey(_LOCAL); + expect(() => childInjector.get(_LOCAL)).toThrow(); + expect(() => leafInjector.get(_LOCAL)).toThrow(); + }); + }); + }); + }); +} + +var _CHILDREN = new Key(_Local); +var _LOCAL = new Key(_Local); +var _TYPE0 = new Key(_Local); + +class _Children{} +class _Local{} +class _Direct{} +class _Any{} +class _Root{ } +class _Type0{ final _Root root; _Type0(this.root); } +class _Type1{ final _Type0 type0; _Type1(this.type0); } +class _Type2{ final _Type1 type1; _Type2(this.type1); } +class _Type3{ final _Type2 type2; _Type3(this.type2); } +class _Type4{ final _Type3 type3; _Type4(this.type3); } +class _Type5{ final _Type4 type4; _Type5(this.type4); } +class _Type6{ final _Type5 type5; _Type6(this.type5); } +class _Type7{ final _Type6 type6; _Type7(this.type6); } +class _Type8{ final _Type7 type7; _Type8(this.type7); } +class _Type9{ final _Type8 type8; _Type9(this.type8); } +class _TypeA{ final _Type9 type9; _TypeA(this.type9); } + diff --git a/test/core_dom/event_handler_spec.dart b/test/core_dom/event_handler_spec.dart index 43b3b9149..d4e077681 100644 --- a/test/core_dom/event_handler_spec.dart +++ b/test/core_dom/event_handler_spec.dart @@ -19,7 +19,7 @@ class FooController { class BarComponent { var invoked = false; BarComponent(RootScope scope) { - scope.context['ctrl'] = this; + scope.context['barComponent'] = this; } } @@ -85,7 +85,7 @@ main() { var shadowRoot = e.shadowRoot; var span = shadowRoot.querySelector('span'); span.dispatchEvent(new CustomEvent('abc')); - var ctrl = _.rootScope.context['ctrl']; + BarComponent ctrl = _.rootScope.context['barComponent']; expect(ctrl.invoked).toEqual(true); })); @@ -102,7 +102,8 @@ main() { document.querySelector('[on-abc]').dispatchEvent(new Event('abc')); var shadowRoot = document.querySelector('bar').shadowRoot; var shadowRootScope = _.getScope(shadowRoot); - expect(shadowRootScope.context['ctrl'].invoked).toEqual(false); + BarComponent ctrl = shadowRootScope.context['ctrl']; + expect(ctrl.invoked).toEqual(false); var fooScope = _.getScope(document.querySelector('[foo]')); expect(fooScope.context['ctrl'].invoked).toEqual(true); diff --git a/test/core_dom/mustache_spec.dart b/test/core_dom/mustache_spec.dart index ba47f09b6..7d7d75ad6 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(injector); + var view = template(rootScope, injector.get(DirectiveInjector)); element = view.nodes; @@ -27,7 +27,7 @@ main() { })); describe('observe/flush phase', () { - it('should first only when then value has settled', async((Logger log) { + it('should fire only when then value has settled', async((Logger log) { _.compile('
'); _.rootScope.apply(); @@ -53,7 +53,7 @@ main() { rootScope.context['name'] = 'OK'; rootScope.context['age'] = 23; - var view = template(injector); + var view = template(rootScope, injector.get(DirectiveInjector)); element = view.nodes[0]; @@ -72,7 +72,7 @@ main() { rootScope.context['line1'] = 'L1'; rootScope.context['line2'] = 'L2'; - var view = template(injector); + var view = template(rootScope, injector.get(DirectiveInjector)); element = view.nodes[0]; @@ -87,7 +87,7 @@ main() { { var element = es('
{{"World" | hello}}
'); var template = compile(element, directives); - var view = template(injector); + var view = template(rootScope, injector.get(DirectiveInjector)); rootScope.apply(); element = view.nodes; diff --git a/test/core_dom/view_spec.dart b/test/core_dom/view_spec.dart index c9bd376bc..3be620b3e 100644 --- a/test/core_dom/view_spec.dart +++ b/test/core_dom/view_spec.dart @@ -63,7 +63,7 @@ class BFormatter { main() { var viewFactoryFactory = (a,b,c,d) => new WalkingViewFactory(a,b,c,d); describe('View', () { - var anchor; + ViewPort viewPort; Element rootElement; var viewCache; @@ -77,32 +77,36 @@ main() { beforeEach((Injector injector, Profiler perf) { rootElement.innerHtml = ''; - anchor = new ViewPort(rootElement.childNodes[0], + var scope = injector.get(Scope); + viewPort = new ViewPort(injector.get(DirectiveInjector), scope, rootElement.childNodes[0], injector.get(Animate)); - a = (viewFactoryFactory(es('Aa'), [], perf, expando))(injector); - b = (viewFactoryFactory(es('Bb'), [], perf, expando))(injector); + a = (viewFactoryFactory(es('Aa'), [], perf, expando))(scope, injector.get(DirectiveInjector)); + b = (viewFactoryFactory(es('Bb'), [], perf, expando))(scope, injector.get(DirectiveInjector)); }); describe('insertAfter', () { - it('should insert block after anchor view', () { - anchor.insert(a); + it('should insert block after anchor view', (RootScope scope) { + viewPort.insert(a); + scope.flush(); expect(rootElement).toHaveHtml('Aa'); }); - it('should insert multi element view after another multi element view', () { - anchor.insert(a); - anchor.insert(b, insertAfter: a); + it('should insert multi element view after another multi element view', (RootScope scope) { + viewPort.insert(a); + viewPort.insert(b, insertAfter: a); + scope.flush(); expect(rootElement).toHaveHtml('AaBb'); }); - it('should insert multi element view before another multi element view', () { - anchor.insert(b); - anchor.insert(a); + it('should insert multi element view before another multi element view', (RootScope scope) { + viewPort.insert(b); + viewPort.insert(a); + scope.flush(); expect(rootElement).toHaveHtml('AaBb'); }); @@ -110,20 +114,23 @@ main() { describe('remove', () { - beforeEach(() { - anchor.insert(a); - anchor.insert(b, insertAfter: a); + beforeEach((RootScope scope) { + viewPort.insert(a); + viewPort.insert(b, insertAfter: a); + scope.flush(); expect(rootElement.text).toEqual('AaBb'); }); - it('should remove the last view', () { - anchor.remove(b); + it('should remove the last view', (RootScope scope) { + viewPort.remove(b); + scope.flush(); expect(rootElement).toHaveHtml('Aa'); }); - it('should remove child views from parent pseudo black', () { - anchor.remove(a); + it('should remove child views from parent pseudo black', (RootScope scope) { + viewPort.remove(a); + scope.flush(); expect(rootElement).toHaveHtml('Bb'); }); @@ -176,16 +183,18 @@ main() { describe('moveAfter', () { - beforeEach(() { - anchor.insert(a); - anchor.insert(b, insertAfter: a); + beforeEach((RootScope scope) { + viewPort.insert(a); + viewPort.insert(b, insertAfter: a); + scope.flush(); expect(rootElement.text).toEqual('AaBb'); }); - it('should move last to middle', () { - anchor.move(a, moveAfter: b); + it('should move last to middle', (RootScope scope) { + viewPort.move(a, moveAfter: b); + scope.flush(); expect(rootElement).toHaveHtml('BbAa'); }); }); @@ -193,7 +202,7 @@ main() { describe('deferred', () { - it('should load directives/formatters from the child injector', () { + it('should load directives/formatters from the child injector', (RootScope scope) { Module rootModule = new Module() ..bind(Probe) ..bind(Log) @@ -209,7 +218,7 @@ main() { Compiler compiler = rootInjector.get(Compiler); DirectiveMap directives = rootInjector.get(DirectiveMap); - compiler(es('{{\'a\' | formatterA}}'), directives)(rootInjector); + compiler(es('{{\'a\' | formatterA}}'), directives)(rootScope, rootInjector.get(DirectiveInjector)); rootScope.apply(); expect(log.log, equals(['AFormatter', 'ADirective'])); @@ -219,11 +228,12 @@ main() { ..bind(BFormatter) ..bind(BDirective); - var childInjector = forceNewDirectivesAndFormatters(rootInjector, [childModule]); + var childInjector = forceNewDirectivesAndFormatters(rootInjector, null, [childModule]); DirectiveMap newDirectives = childInjector.get(DirectiveMap); + var scope = childInjector.get(Scope); compiler(es('{{\'a\' | formatterA}}' - '{{\'b\' | formatterB}}'), newDirectives)(childInjector); + '{{\'b\' | formatterB}}'), newDirectives)(scope, childInjector.get(DirectiveInjector)); 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 e1a0ad665..019987b04 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)(injector, element); + compiler(element, directives)(scope, injector.get(DirectiveInjector), element); scope.context['htmlVar'] = 'Google!'; scope.apply(); // Sanitization removes the href attribute on the tag. @@ -18,7 +18,7 @@ main() { describe('injected NodeValidator', () { beforeEachModule((Module module) { - module.bind(dom.NodeValidator, toFactory: (_) { + module.bind(dom.NodeValidator, toFactory: () { final validator = new NodeValidatorBuilder(); validator.allowNavigation(new AnyUriPolicy()); validator.allowTextElements(); @@ -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)(injector, element); + compiler(element, directives)(scope, injector.get(DirectiveInjector), 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 af65f7f9d..3d7468acf 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)(injector, [element]); + compiler([element], directives)(scope, injector.get(DirectiveInjector), [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 1821f217a..a0cb7624e 100644 --- a/test/directive/ng_form_spec.dart +++ b/test/directive/ng_form_spec.dart @@ -722,9 +722,9 @@ void main() { }); it('should be resolvable by injector if configured by user.', - (Injector injector, Compiler compiler, DirectiveMap directives) { + (Scope scope, Injector injector, Compiler compiler, DirectiveMap directives) { var element = es('
'); - expect(() => compiler(element, directives)(injector, element)) + expect(() => compiler(element, directives)(scope, injector.get(DirectiveInjector), element)) .not.toThrow(); }); }); diff --git a/test/directive/ng_if_spec.dart b/test/directive/ng_if_spec.dart index 0f84d59d8..ff016a9a9 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)(injector, [element]); + compiler([element], _directives)(scope, injector.get(DirectiveInjector), [element]); scope.apply(applyFn); }; directives = _directives; diff --git a/test/directive/ng_model_spec.dart b/test/directive/ng_model_spec.dart index 76e485919..b952f9408 100644 --- a/test/directive/ng_model_spec.dart +++ b/test/directive/ng_model_spec.dart @@ -36,6 +36,7 @@ bool isBrowser(String pattern) => dom.window.navigator.userAgent.indexOf(pattern void main() { describe('ng-model', () { TestBed _; + DirectiveInjector dirInjector; beforeEachModule((Module module) { module @@ -44,7 +45,10 @@ void main() { ..bind(CountingValidator); }); - beforeEach((TestBed tb) => _ = tb); + beforeEach((TestBed tb) { + _ = tb; + dirInjector = new DirectiveInjector(null, _.injector, null, null, null, null, null); + }); describe('type="text" like', () { it('should update input value from model', () { @@ -94,7 +98,7 @@ void main() { var ngModelOptions = new NgModelOptions(); nodeAttrs['ng-model'] = 'model'; - var model = new NgModel(scope, ngElement, i.createChild([new Module()]), + var model = new NgModel(scope, ngElement, dirInjector, nodeAttrs, new Animate(), null); dom.querySelector('body').append(element); var input = new InputTextLike(element, model, scope, ngModelOptions); @@ -374,7 +378,7 @@ void main() { var ngModelOptions = new NgModelOptions(); nodeAttrs['ng-model'] = 'model'; - var model = new NgModel(scope, ngElement, i.createChild([new Module()]), + var model = new NgModel(scope, ngElement, dirInjector, nodeAttrs, new Animate(), null); dom.querySelector('body').append(element); var input = new InputTextLike(element, model, scope, ngModelOptions); @@ -466,7 +470,7 @@ void main() { var ngModelOptions = new NgModelOptions(); nodeAttrs['ng-model'] = 'model'; - var model = new NgModel(scope, ngElement, i.createChild([new Module()]), + var model = new NgModel(scope, ngElement, dirInjector, nodeAttrs, new Animate(), null); dom.querySelector('body').append(element); var input = new InputTextLike(element, model, scope, ngModelOptions); @@ -571,7 +575,7 @@ void main() { var ngModelOptions = new NgModelOptions(); nodeAttrs['ng-model'] = 'model'; - var model = new NgModel(scope, ngElement, i.createChild([new Module()]), + var model = new NgModel(scope, ngElement, dirInjector, nodeAttrs, new Animate(), null); dom.querySelector('body').append(element); var input = new InputTextLike(element, model, scope, ngModelOptions); @@ -782,7 +786,7 @@ void main() { var ngModelOptions = new NgModelOptions(); nodeAttrs['ng-model'] = 'model'; - var model = new NgModel(scope, ngElement, i.createChild([new Module()]), + var model = new NgModel(scope, ngElement, dirInjector, nodeAttrs, new Animate(), null); dom.querySelector('body').append(element); var input = new InputTextLike(element, model, scope, ngModelOptions); diff --git a/test/directive/ng_non_bindable_spec.dart b/test/directive/ng_non_bindable_spec.dart index a2ec3fb3d..1a2817360 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)(injector, [element]); + compiler([element], directives)(scope, injector.get(DirectiveInjector), [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 4b35f9528..160424e9d 100644 --- a/test/directive/ng_repeat_spec.dart +++ b/test/directive/ng_repeat_spec.dart @@ -20,12 +20,12 @@ main() { scope = rootScope; compile = (html, [scope]) { element = e(html); - var viewFactory = compiler([element], _directives); - var blockInjector = injector; + ViewFactory viewFactory = compiler([element], _directives); + Injector blockInjector = injector; if (scope != null) { - viewFactory.bind(injector)(scope); + viewFactory.bind(injector.get(DirectiveInjector))(scope); } else { - viewFactory(injector, [element]); + viewFactory(rootScope, injector.get(DirectiveInjector), [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(injector, element); + View view = viewFactory(scope, injector.get(DirectiveInjector), 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(injector, element); + View view = viewFactory(scope, injector.get(DirectiveInjector), 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(injector, element); + View view = viewFactory(scope, injector.get(DirectiveInjector), element); scope.context['items'] = ['a', 'b'].map((i) => i); // makes an iterable scope.apply(); expect(element).toHaveText('ab'); @@ -531,23 +531,20 @@ main() { }); it(r'should not move blocks when elements only added or removed', - inject((Injector injector) { + inject((Injector injector, Scope rootScope, Compiler compiler, + DirectiveMap _directives, ExceptionHandler exceptionHandler) { var throwOnMove = new MockAnimate(); var child = injector.createChild( [new Module()..bind(Animate, toValue: throwOnMove)]); - child.invoke((Injector injector, Scope rootScope, Compiler compiler, - DirectiveMap _directives) { - exceptionHandler = injector.get(ExceptionHandler); - scope = rootScope; - compile = (html) { - element = e(html); - var viewFactory = compiler([element], _directives); - viewFactory(injector, [element]); - return element; - }; - directives = _directives; - }); + scope = rootScope; + compile = (html) { + element = e(html); + var viewFactory = compiler([element], _directives); + viewFactory(scope, child.get(DirectiveInjector), [element]); + return element; + }; + directives = _directives; element = compile( '
    ' diff --git a/test/introspection_spec.dart b/test/introspection_spec.dart index 441abfaa2..a034d7073 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.parent).toBe(_.injector); - expect(ngInjector(_.rootElement).parent).toBe(_.injector); + expect(probe.injector.appInjector).toBe(_.injector); + expect(ngInjector(_.rootElement).appInjector).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/io/expression_extractor_spec.dart b/test/io/expression_extractor_spec.dart index f9ce6a0c4..d8b09da74 100644 --- a/test/io/expression_extractor_spec.dart +++ b/test/io/expression_extractor_spec.dart @@ -1,7 +1,6 @@ library ng.tool.expression_extractor_spec; import 'package:di/di.dart'; -import 'package:di/dynamic_injector.dart'; import 'package:angular/tools/common.dart'; import 'package:angular/tools/io.dart'; import 'package:angular/tools/io_impl.dart'; @@ -17,8 +16,7 @@ void main() { Iterable _extractExpressions(file) { Module module = new Module(); - Injector injector = new DynamicInjector(modules: [module], - allowImplicitInjection: true); + Injector injector = new ModuleInjector([module]); IoService ioService = new IoServiceImpl(); var sourceCrawler = new SourceCrawlerImpl(['packages/']); diff --git a/test/routing/ng_view_spec.dart b/test/routing/ng_view_spec.dart index 1709ff4f8..34bffa8f8 100644 --- a/test/routing/ng_view_spec.dart +++ b/test/routing/ng_view_spec.dart @@ -49,8 +49,9 @@ main() => describe('ngView', () { router.route('/foo'); microLeap(); _.rootScope.apply(); + Probe probe = _.rootScope.context['p']; - expect(_.rootScope.context['p'].injector.get(RouteProvider) is NgView).toBeTruthy(); + expect(probe.injector.get(RouteProvider) is NgView).toBeTruthy(); })); diff --git a/test/tools/transformer/static_angular_generator_spec.dart b/test/tools/transformer/static_angular_generator_spec.dart index 8346c6a34..506467c75 100644 --- a/test/tools/transformer/static_angular_generator_spec.dart +++ b/test/tools/transformer/static_angular_generator_spec.dart @@ -43,12 +43,11 @@ import 'package:angular/application_factory_static.dart'; import 'package:di/di.dart' show Module; import 'main_static_expressions.dart' as generated_static_expressions; import 'main_static_metadata.dart' as generated_static_metadata; -import 'main_static_injector.dart' as generated_static_injector; class MyModule extends Module {} main() { - var app = staticApplicationFactory(generated_static_injector.factories, generated_static_metadata.typeAnnotations, generated_static_expressions.getters, generated_static_expressions.setters, generated_static_expressions.symbols) + var app = staticApplicationFactory(generated_static_metadata.typeAnnotations, generated_static_expressions.getters, generated_static_expressions.setters, generated_static_expressions.symbols) .addModule(new MyModule()) .run(); } @@ -80,12 +79,11 @@ import 'package:angular/application_factory_static.dart' as ng; import 'package:di/di.dart' show Module; import 'main_static_expressions.dart' as generated_static_expressions; import 'main_static_metadata.dart' as generated_static_metadata; -import 'main_static_injector.dart' as generated_static_injector; class MyModule extends Module {} main() { - var app = ng.staticApplicationFactory(generated_static_injector.factories, generated_static_metadata.typeAnnotations, generated_static_expressions.getters, generated_static_expressions.setters, generated_static_expressions.symbols) + var app = ng.staticApplicationFactory(generated_static_metadata.typeAnnotations, generated_static_expressions.getters, generated_static_expressions.setters, generated_static_expressions.symbols) .addModule(new MyModule()) .run(); }