From 494deda594f39b422e4c2f5def1a2cbaf749efba Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 11 Jul 2014 11:56:26 -0700 Subject: [PATCH] perf(View): Improve View instantiation speed and memory consumption. View contains many injectors which are expensive in both speed and memory. The New DirectiveInjector assumes that there are no more than 10 directives per element and can be a lot more efficient than a array/hash lookup and those it can be faster as well as smaller. This change makes View instantiation speed 4x faster in Dartium VM. BREAKING CHANGE: - Injector no longer supports visibility - The Directive:module instead of returning Module now takes DirectiveModule (which supports visibility) - Application Injector and DirectiveInjector now have separate trees. (The root if DirectiveInjector is ApplicationInjector) --- benchmark/pubspec.lock | 14 +- benchmark/pubspec.yaml | 1 + benchmark/web/tree.dart | 1 + bin/parser_generator_for_spec.dart | 4 +- lib/angular.dart | 4 +- lib/animate/module.dart | 1 + lib/application.dart | 13 +- lib/application_factory.dart | 3 - lib/application_factory_static.dart | 26 +- lib/cache/js_cache_register.dart | 1 + lib/cache/module.dart | 5 +- lib/change_detection/ast_parser.dart | 1 + lib/core/annotation.dart | 2 +- lib/core/annotation_src.dart | 65 +-- lib/core/formatter.dart | 3 +- lib/core/module.dart | 3 + lib/core/module_internal.dart | 1 + lib/core/parser/dynamic_parser.dart | 1 + lib/core/parser/lexer.dart | 1 + lib/core/registry_static.dart | 2 +- lib/core/static_keys.dart | 3 +- lib/core_dom/common.dart | 32 +- lib/core_dom/directive_injector.dart | 435 ++++++++++++++++++ lib/core_dom/directive_map.dart | 2 +- lib/core_dom/element_binder.dart | 145 ++---- lib/core_dom/module_internal.dart | 17 +- .../shadow_dom_component_factory.dart | 46 +- lib/core_dom/tagging_view_factory.dart | 62 ++- .../transcluding_component_factory.dart | 43 +- lib/core_dom/view.dart | 41 +- lib/core_dom/view_factory.dart | 48 +- lib/directive/module.dart | 3 + lib/directive/ng_base_css.dart | 4 +- lib/directive/ng_control.dart | 2 +- lib/directive/ng_form.dart | 7 +- lib/directive/ng_if.dart | 39 +- lib/directive/ng_include.dart | 11 +- lib/directive/ng_model.dart | 28 +- lib/directive/ng_model_select.dart | 5 +- lib/directive/ng_repeat.dart | 2 - lib/directive/ng_switch.dart | 1 - lib/introspection.dart | 5 +- lib/introspection_js.dart | 62 +++ lib/mock/module.dart | 3 +- lib/mock/probe.dart | 3 +- lib/mock/test_bed.dart | 10 +- lib/mock/test_injection.dart | 32 +- lib/perf/module.dart | 2 +- lib/playback/playback_http.dart | 1 + lib/routing/module.dart | 1 + lib/routing/ng_bind_route.dart | 9 +- lib/routing/ng_view.dart | 36 +- lib/tools/expression_extractor.dart | 3 +- .../transformer/expression_generator.dart | 16 +- lib/tools/transformer/options.dart | 2 +- .../transformer/static_angular_generator.dart | 4 - lib/transformer.dart | 11 +- pubspec.lock | 14 +- pubspec.yaml | 8 +- test/_specs.dart | 1 - test/angular_spec.dart | 30 +- test/core/annotation_src_spec.dart | 6 +- test/core/templateurl_spec.dart | 24 +- test/core_dom/compiler_spec.dart | 68 ++- test/core_dom/directive_injector_spec.dart | 122 +++++ test/core_dom/event_handler_spec.dart | 7 +- test/core_dom/mustache_spec.dart | 10 +- test/core_dom/view_spec.dart | 66 +-- test/directive/ng_bind_html_spec.dart | 6 +- test/directive/ng_bind_spec.dart | 2 +- test/directive/ng_form_spec.dart | 4 +- test/directive/ng_if_spec.dart | 2 +- test/directive/ng_model_spec.dart | 16 +- test/directive/ng_non_bindable_spec.dart | 2 +- test/directive/ng_repeat_spec.dart | 37 +- test/introspection_spec.dart | 4 +- test/io/expression_extractor_spec.dart | 4 +- test/routing/ng_view_spec.dart | 3 +- .../static_angular_generator_spec.dart | 6 +- 79 files changed, 1216 insertions(+), 554 deletions(-) create mode 100644 lib/core_dom/directive_injector.dart create mode 100644 lib/introspection_js.dart create mode 100644 test/core_dom/directive_injector_spec.dart 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(); }