From 7ace1dd5be4f8d9493310f86e539c43c14c146ad Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Wed, 9 Jul 2014 12:08:08 -0700 Subject: [PATCH 1/5] chore: remove noSuchMethod which improves dart2js size by about 100k --- lib/core_dom/shadowless_shadow_root.dart | 67 +++++++++++++++++++++--- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/lib/core_dom/shadowless_shadow_root.dart b/lib/core_dom/shadowless_shadow_root.dart index d4994a458..ae4a87b62 100644 --- a/lib/core_dom/shadowless_shadow_root.dart +++ b/lib/core_dom/shadowless_shadow_root.dart @@ -1,12 +1,65 @@ part of angular.core.dom_internal; - -@proxy class ShadowlessShadowRoot implements dom.ShadowRoot { dom.Element _element; - ShadowlessShadowRoot(this._element); - - noSuchMethod(Invocation invocation) { - throw new UnimplementedError("Not yet implemented in ShadowlessShadowRoot."); - } + _notSupported() { throw new UnsupportedError("Not supported"); } + dom.Element get activeElement => _notSupported(); + dom.Element get host => _notSupported(); + String get innerHtml => _notSupported(); + void set innerHtml(String value) => _notSupported(); + dom.ShadowRoot get olderShadowRoot => _notSupported(); + bool get _resetStyleInheritance => _notSupported(); + void set _resetStyleInheritance(bool value) => _notSupported(); + List get styleSheets => _notSupported(); + dom.Node clone(bool deep) => _notSupported(); + dom.Element elementFromPoint(int x, int y) => _notSupported(); + dom.Element getElementById(String elementId) => _notSupported(); + List getElementsByClassName(String className) => _notSupported(); + List getElementsByTagName(String tagName) => _notSupported(); + dom.Selection getSelection() => _notSupported(); + bool get resetStyleInheritance { _notSupported(); } + void set resetStyleInheritance(bool value) { _notSupported(); } + bool get applyAuthorStyles { _notSupported(); } + void set applyAuthorStyles(bool value) { _notSupported(); } + List get children => _notSupported(); + void set children(List value) { _notSupported(); } + dom.ElementList querySelectorAll(String selectors) => _notSupported(); + void setInnerHtml(String html, {dom.NodeValidator validator, dom.NodeTreeSanitizer treeSanitizer}) { _notSupported(); } + void appendText(String text) { _notSupported(); } + void appendHtml(String text) { _notSupported(); } + dom.Element query(String relativeSelectors) { _notSupported(); } + dom.ElementList queryAll(String relativeSelectors) { _notSupported(); } + dom.Element querySelector(String selectors) => _notSupported(); + List get nodes => _notSupported(); + void set nodes(Iterable value) { _notSupported(); } + void remove() { _notSupported(); } + dom.Node replaceWith(dom.Node otherNode) { _notSupported(); } + dom.Node insertAllBefore(Iterable newNodes, dom.Node refChild) { _notSupported(); } + void _clearChildren() { _notSupported(); } + String get baseUri => _notSupported(); + List get childNodes => _notSupported(); + dom.Node get firstChild => _notSupported(); + dom.Node get lastChild => _notSupported(); + String get _localName => _notSupported(); + String get _namespaceUri => _notSupported(); + dom.Node get nextNode => _notSupported(); + String get nodeName => _notSupported(); + int get nodeType => _notSupported(); + String get nodeValue => _notSupported(); + dom.Document get ownerDocument => _notSupported(); + dom.Element get parent => _notSupported(); + dom.Node get parentNode => _notSupported(); + dom.Node get previousNode => _notSupported(); + String get text => _notSupported(); + void set text(String value) => _notSupported(); + dom.Node append(dom.Node newChild) => _notSupported(); + bool contains(dom.Node other) => _notSupported(); + bool hasChildNodes() => _notSupported(); + dom.Node insertBefore(dom.Node newChild, dom.Node refChild) => _notSupported(); + dom.Node _removeChild(dom.Node oldChild) => _notSupported(); + dom.Node _replaceChild(dom.Node newChild, dom.Node oldChild) => _notSupported(); + dom.Events get on => _notSupported(); + void addEventListener(String type, dom.EventListener listener, [bool useCapture]) => _notSupported(); + bool dispatchEvent(dom.Event event) => _notSupported(); + void removeEventListener(String type, dom.EventListener listener, [bool useCapture]) => _notSupported(); } From 896c134461e2668aea91a79ab43397dbc18d5800 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Tue, 1 Jul 2014 23:54:54 -0700 Subject: [PATCH 2/5] fix: incorrect mirrors import --- benchmark/watch_group_perf.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/watch_group_perf.dart b/benchmark/watch_group_perf.dart index 4eb9c065f..99bf17f69 100644 --- a/benchmark/watch_group_perf.dart +++ b/benchmark/watch_group_perf.dart @@ -9,7 +9,7 @@ import 'package:benchmark_harness/benchmark_harness.dart'; @MirrorsUsed( targets: const [ - 'angular.perf.watch_group' + 'angular.benchmarks.watch_group' ], override: '*' ) From 057985d0d45d85e7996686fb4d8f6d9a6aead718 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Tue, 1 Jul 2014 23:56:08 -0700 Subject: [PATCH 3/5] perf: fewer string concatenations (10% improvement) --- lib/change_detection/watch_group.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/change_detection/watch_group.dart b/lib/change_detection/watch_group.dart index 830fa20d8..2be94c1a7 100644 --- a/lib/change_detection/watch_group.dart +++ b/lib/change_detection/watch_group.dart @@ -672,10 +672,11 @@ abstract class _ArgHandler extends _Handler { } class _PositionalArgHandler extends _ArgHandler { + static final List _ARGS = new List.generate(20, (index) => 'arg[$index]'); final int index; _PositionalArgHandler(WatchGroup watchGrp, _EvalWatchRecord record, int index) : this.index = index, - super(watchGrp, 'arg[$index]', record); + super(watchGrp, _ARGS[index], record); void acceptValue(object) { watchRecord.dirtyArgs = true; @@ -684,11 +685,17 @@ class _PositionalArgHandler extends _ArgHandler { } class _NamedArgHandler extends _ArgHandler { + static final Map _NAMED_ARG = new HashMap(); + static String _GET_NAMED_ARG(Symbol symbol) { + String name = _NAMED_ARG[symbol]; + if (name == null) name = _NAMED_ARG[symbol] = 'namedArg[$name]'; + return name; + } final Symbol name; _NamedArgHandler(WatchGroup watchGrp, _EvalWatchRecord record, Symbol name) : this.name = name, - super(watchGrp, 'namedArg[$name]', record); + super(watchGrp, _GET_NAMED_ARG(name), record); void acceptValue(object) { if (watchRecord.namedArgs == null) { From cae6970da101c7c806b8af7449def9859faa55a3 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 16 Jun 2014 22:46:46 -0700 Subject: [PATCH 4/5] 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 | 4 +- bin/parser_generator_for_spec.dart | 16 +- lib/application.dart | 15 +- lib/application_factory.dart | 3 - lib/application_factory_static.dart | 25 +- lib/change_detection/ast_parser.dart | 1 + lib/core/annotation.dart | 4 +- lib/core/annotation_src.dart | 66 ++- lib/core/formatter.dart | 33 +- lib/core/module.dart | 3 + lib/core/module_internal.dart | 12 +- lib/core/parser/dynamic_parser.dart | 1 + lib/core/parser/lexer.dart | 1 + lib/core/registry.dart | 70 --- lib/core/registry_static.dart | 2 +- lib/core/scope.dart | 1 + 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 | 42 +- lib/core_dom/element_binder.dart | 128 ++---- lib/core_dom/module_internal.dart | 11 +- lib/core_dom/selector.dart | 18 +- .../shadow_dom_component_factory.dart | 46 +- lib/core_dom/tagging_view_factory.dart | 56 ++- .../transcluding_component_factory.dart | 43 +- lib/core_dom/view.dart | 41 +- lib/core_dom/view_factory.dart | 49 +- lib/directive/module.dart | 5 +- 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 | 12 +- lib/directive/ng_model_select.dart | 5 +- lib/directive/ng_repeat.dart | 2 - lib/directive/ng_switch.dart | 1 - lib/introspection.dart | 3 +- lib/introspection_js.dart | 2 +- lib/metadata.dart | 0 lib/mock/module.dart | 5 +- lib/mock/probe.dart | 8 +- lib/mock/test_bed.dart | 10 +- lib/mock/test_injection.dart | 39 +- lib/playback/playback_http.dart | 1 + lib/routing/module.dart | 8 +- lib/routing/ng_bind_route.dart | 9 +- lib/routing/ng_view.dart | 36 +- lib/tools/expression_extractor.dart | 1 - .../transformer/expression_generator.dart | 15 +- lib/tools/transformer/options.dart | 2 +- .../transformer/static_angular_generator.dart | 4 - lib/transformer.dart | 11 +- pubspec.lock | 8 +- pubspec.yaml | 8 +- test/_specs.dart | 3 +- test/angular_spec.dart | 31 +- test/core/annotation_src_spec.dart | 6 +- test/core/core_directive_spec.dart | 18 +- .../parser/generated_getter_setter_spec.dart | 2 +- test/core/parser/parser_spec.dart | 12 +- test/core/registry_spec.dart | 64 --- test/core/templateurl_spec.dart | 24 +- test/core_dom/compiler_spec.dart | 86 +++- 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/selector_spec.dart | 124 ++--- test/core_dom/view_spec.dart | 68 +-- test/directive/ng_bind_html_spec.dart | 6 +- test/directive/ng_bind_spec.dart | 2 +- test/directive/ng_form_spec.dart | 6 +- 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/formatter/currency_spec.dart | 2 +- test/formatter/date_spec.dart | 2 +- test/formatter/filter_spec.dart | 2 +- test/formatter/number_spec.dart | 2 +- test/introspection_spec.dart | 4 +- test/io/expression_extractor_spec.dart | 4 +- test/routing/ng_bind_route_spec.dart | 8 +- test/routing/ng_view_spec.dart | 3 +- test/routing/routing_spec.dart | 2 +- .../static_angular_generator_spec.dart | 6 +- 89 files changed, 1343 insertions(+), 774 deletions(-) create mode 100644 lib/core_dom/directive_injector.dart delete mode 100644 lib/metadata.dart delete mode 100644 test/core/registry_spec.dart create mode 100644 test/core_dom/directive_injector_spec.dart diff --git a/benchmark/pubspec.lock b/benchmark/pubspec.lock index 4fceed7bd..987fc6d0b 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.6" 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 b33ec5391..0fa2e84a8 100644 --- a/benchmark/web/tree.dart +++ b/benchmark/web/tree.dart @@ -1,4 +1,5 @@ import 'package:di/di.dart'; +import 'package:di/di_dynamic.dart'; import 'package:angular/angular.dart'; import 'package:angular/core_dom/module_internal.dart'; import 'package:angular/application_factory.dart'; @@ -246,6 +247,7 @@ class NgFreeTreeClass implements ShadowRootAware { // Main function runs the benchmark. main() { + setupModuleTypeReflector(); var cleanup, createDom; var module = new Module() @@ -254,7 +256,7 @@ main() { ..bind(NgFreeTree) ..bind(NgFreeTreeScoped) ..bind(NgFreeTreeClass) - ..bind(ScopeDigestTTL, toFactory: (_) => new ScopeDigestTTL.value(15)) + ..bind(ScopeDigestTTL, toFactory: () => new ScopeDigestTTL.value(15)) ..bind(CompilerConfig, toValue: new CompilerConfig.withOptions(elementProbeEnabled: false)); var injector = applicationFactory().addModule(module).run(); diff --git a/bin/parser_generator_for_spec.dart b/bin/parser_generator_for_spec.dart index ba1e3a8f4..020770b87 100644 --- a/bin/parser_generator_for_spec.dart +++ b/bin/parser_generator_for_spec.dart @@ -1,15 +1,21 @@ import 'dart:io' as io; import 'package:di/di.dart'; -import 'package:di/dynamic_injector.dart'; +import 'package:di/di_dynamic.dart'; +import 'package:angular/cache/module.dart'; +import 'package:angular/core/parser/lexer.dart'; import 'package:angular/core/parser/parser.dart'; import 'package:angular/tools/parser_getter_setter/generator.dart'; main(arguments) { - Module module = new Module()..bind(Parser, toFactory: (i) => i.get(DynamicParser)); - module.bind(ParserBackend, toFactory: (i) => i.get(DartGetterSetterGen)); - Injector injector = new DynamicInjector(modules: [module], - allowImplicitInjection: true); + setupModuleTypeReflector(); + Module module = new Module() + ..bind(Lexer) + ..bind(ParserGetterSetter) + ..bind(Parser, toImplementation: DynamicParser) + ..install(new CacheModule()); + module.bind(ParserBackend, toImplementation: DartGetterSetterGen); + 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/application.dart b/lib/application.dart index 14a0d354a..5392ab790 100644 --- a/lib/application.dart +++ b/lib/application.dart @@ -84,6 +84,7 @@ import 'package:angular/routing/module.dart'; import 'package:angular/introspection_js.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); } } @@ -152,7 +153,7 @@ abstract class Application { modules.add(ngModule); ngModule..bind(VmTurnZone, toValue: zone) ..bind(Application, toValue: this) - ..bind(dom.Node, toFactory: (i) => i.getByKey(new Key(Application)).element); + ..bind(dom.Node, toFactory: (Application app) => app.element, inject: [Application]); } /** @@ -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..9a85aead2 100644 --- a/lib/application_factory_static.dart +++ b/lib/application_factory_static.dart @@ -30,7 +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'; @@ -46,9 +45,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 +55,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 +75,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/change_detection/ast_parser.dart b/lib/change_detection/ast_parser.dart index 46129ee5a..77444e899 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/di.dart' show Injectable; 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 768def9aa..cee2ad10f 100644 --- a/lib/core/annotation.dart +++ b/lib/core/annotation.dart @@ -11,12 +11,14 @@ export "package:angular/core/annotation_src.dart" show ShadowRootAware, Formatter, - Injectable, + DirectiveBinder, + DirectiveBinderFn, Directive, Component, Controller, Decorator, + Visibility, DirectiveAnnotation, NgAttr, diff --git a/lib/core/annotation_src.dart b/lib/core/annotation_src.dart index 2458fd8d9..4f54a9f87 100644 --- a/lib/core/annotation_src.dart +++ b/lib/core/annotation_src.dart @@ -1,36 +1,28 @@ library angular.core.annotation_src; -import "package:di/di.dart" show Injector, Visibility; +import "package:di/di.dart" show Injector, Visibility, Factory; -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; +abstract class DirectiveBinder { + bind(key, {Function toFactory, inject, Visibility visibility: Visibility.LOCAL}); +} -localVisibility (Injector requesting, Injector defining) - => identical(skipShadow(requesting), defining); +typedef void DirectiveBinderFn(DirectiveBinder module); -directChildrenVisibility(Injector requesting, Injector defining) { - requesting = skipShadow(requesting); - return identical(requesting.parent, defining) || localVisibility(requesting, defining); -} +RegExp _ATTR_NAME = new RegExp(r'\[([^\]]+)\]$'); 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'; } /** @@ -39,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. @@ -122,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 @@ -132,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. @@ -215,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 [], @@ -224,10 +219,6 @@ abstract class Directive { }); toString() => selector; - get hashCode => selector.hashCode; - operator==(other) => - other is Directive && selector == other.selector; - Directive _cloneWithNewMap(newMap); } @@ -328,7 +319,7 @@ class Component extends Directive { applyAuthorStyles, resetStyleInheritance, this.publishAs, - module, + DirectiveBinderFn module, map, selector, visibility, @@ -386,7 +377,7 @@ class Decorator extends Directive { const Decorator({children: Directive.COMPILE_CHILDREN, map, selector, - module, + DirectiveBinderFn module, visibility, exportExpressions, exportExpressionAttrs}) @@ -439,7 +430,7 @@ class Controller extends Decorator { children: Directive.COMPILE_CHILDREN, this.publishAs, map, - module, + DirectiveBinderFn module, selector, visibility, exportExpressions, @@ -588,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 25f317414..e88e01f65 100644 --- a/lib/core/formatter.dart +++ b/lib/core/formatter.dart @@ -1,5 +1,6 @@ library angular.core_internal.formatter_map; +import 'dart:collection'; import 'package:di/di.dart'; import 'package:angular/core/annotation_src.dart'; import 'package:angular/core/registry.dart'; @@ -8,16 +9,30 @@ import 'package:angular/core/registry.dart'; * Registry of formatters at runtime. */ @Injectable() -class FormatterMap extends AnnotationMap { - Injector _injector; - FormatterMap(Injector injector, MetadataExtractor extractMetadata) - : this._injector = injector, - super(injector, extractMetadata); +class FormatterMap { + final Map _map = new HashMap(); + final Injector _injector; - call(String name) { - var formatter = new Formatter(name: name); - var formatterType = this[formatter]; - return _injector.get(formatterType); + FormatterMap(this._injector, MetadataExtractor extractMetadata) { + (_injector as ModuleInjector).types.forEach((type) { + extractMetadata(type) + .where((annotation) => annotation is Formatter) + .forEach((Formatter formatter) { + _map[formatter.name] = type; + }); + }); + } + + call(String name) => _injector.get(this[name]); + + Type operator[](String name) { + Type formatterType = _map[name]; + if (formatterType == null) throw "No formatter '$name' found!"; + return formatterType; + } + + void forEach(fn(K, Type)) { + _map.forEach(fn); } } 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 2036b6482..994abcc17 100644 --- a/lib/core/module_internal.dart +++ b/lib/core/module_internal.dart @@ -35,21 +35,19 @@ class CoreModule extends Module { CoreModule() { bind(ScopeDigestTTL); - bind(MetadataExtractor); bind(ExceptionHandler); bind(FormatterMap); bind(Interpolate); bind(RootScope); - bind(Scope, toFactory: (injector) => injector.getByKey(ROOT_SCOPE_KEY)); - bind(ClosureMap, toFactory: (_) => throw "Must provide dynamic/static ClosureMap."); + bind(Scope, inject: [ROOT_SCOPE_KEY]); + bind(ClosureMap, toFactory: () => throw "Must provide dynamic/static ClosureMap."); bind(ScopeStats); bind(ScopeStatsEmitter); - bind(ScopeStatsConfig, toFactory: (i) => new ScopeStatsConfig()); + bind(ScopeStatsConfig); bind(Object, toValue: {}); // RootScope context - bind(VmTurnZone); - bind(Parser, toFactory: (i) => i.get(DynamicParser)); - bind(ParserBackend, toFactory: (i) => i.get(DynamicParserBackend)); + bind(Parser, inject: [DynamicParser]); + bind(ParserBackend, inject: [DynamicParserBackend]); bind(DynamicParser); bind(DynamicParserBackend); bind(Lexer); diff --git a/lib/core/parser/dynamic_parser.dart b/lib/core/parser/dynamic_parser.dart index 26486b15c..8a74e1eba 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/di.dart' show Injectable; 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..3ad848cd4 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/di.dart' show Injectable; import 'package:angular/core/annotation_src.dart'; import 'package:angular/core/parser/characters.dart'; diff --git a/lib/core/registry.dart b/lib/core/registry.dart index 9d66de3ed..1db9cf61c 100644 --- a/lib/core/registry.dart +++ b/lib/core/registry.dart @@ -1,75 +1,5 @@ library angular.core.registry; -import 'package:di/di.dart' show Injector; - -abstract class AnnotationMap { - final Map _map = {}; - - AnnotationMap(Injector injector, MetadataExtractor extractMetadata) { - injector.types.forEach((type) { - extractMetadata(type) - .where((annotation) => annotation is K) - .forEach((annotation) { - _map[annotation] = type; - }); - }); - } - - Type operator[](K annotation) { - var value = _map[annotation]; - if (value == null) throw 'No $annotation found!'; - return value; - } - - void forEach(fn(K, Type)) { - _map.forEach(fn); - } - - List annotationsFor(Type type) { - final res = []; - forEach((ann, annType) { - if (annType == type) res.add(ann); - }); - return res; - } -} - -abstract class AnnotationsMap { - final Map> map = {}; - - AnnotationsMap(Injector injector, MetadataExtractor extractMetadata) { - injector.types.forEach((type) { - extractMetadata(type) - .where((annotation) => annotation is K) - .forEach((annotation) { - map.putIfAbsent(annotation, () => []).add(type); - }); - }); - } - - List operator[](K annotation) { - var value = map[annotation]; - if (value == null) throw 'No $annotation found!'; - return value; - } - - void forEach(fn(K, Type)) { - map.forEach((annotation, types) { - types.forEach((type) { - fn(annotation, type); - }); - }); - } - - List annotationsFor(Type type) { - var res = []; - forEach((ann, annType) { - if (annType == type) res.add(ann); - }); - return res; - } -} - abstract class MetadataExtractor { Iterable call(Type type); } diff --git a/lib/core/registry_static.dart b/lib/core/registry_static.dart index fcb287b13..a62d90b35 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' show Injectable; +import 'package:di/di.dart' show Injectable; import 'package:angular/core/registry.dart'; @Injectable() diff --git a/lib/core/scope.dart b/lib/core/scope.dart index dee53a4d3..7a75b0622 100644 --- a/lib/core/scope.dart +++ b/lib/core/scope.dart @@ -532,6 +532,7 @@ class ScopeStatsEmitter { * ScopeStatsConfig is used to modify behavior of [ScopeStats]. You can use this * object to modify behavior at runtime too. */ +@Injectable() class ScopeStatsConfig { var emit = false; 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 53a2c6c59..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: (i) { - var scope = i.parent.getByKey(SCOPE_KEY); - return scope.createChild(new PrototypeMap(scope.context)); - })); - - 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..b21c14af1 --- /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/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.LOCAL; + 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, [int depth = 0]) { + var oldTag = _TAG_GET.makeCurrent(); + try { + return _getByKey(key, depth); + } on ResolvingError catch (e, s) { + e.appendKey(key); + rethrow; + } finally { + oldTag.makeCurrent(); + } + } + + Object _getByKey(Key key, int depth) { + int uid = key.uid; + if (uid == null || uid == UNDEFINED_ID) return appInjector.getByKey(key, depth + 1); + bool isDirective = uid < 0; + return isDirective ? _getDirectiveByKey(key, uid, depth + 1, appInjector) : _getById(uid); + } + + _getDirectiveByKey(Key k, int visType, int d, Injector i) { + do { + if (_key0 == null) break; if (identical(_key0, k)) return _obj0 == null ? _obj0 = _new(_pKeys0, _factory0, d) : _obj0; + if (_key1 == null) break; if (identical(_key1, k)) return _obj1 == null ? _obj1 = _new(_pKeys1, _factory1, d) : _obj1; + if (_key2 == null) break; if (identical(_key2, k)) return _obj2 == null ? _obj2 = _new(_pKeys2, _factory2, d) : _obj2; + if (_key3 == null) break; if (identical(_key3, k)) return _obj3 == null ? _obj3 = _new(_pKeys3, _factory3, d) : _obj3; + if (_key4 == null) break; if (identical(_key4, k)) return _obj4 == null ? _obj4 = _new(_pKeys4, _factory4, d) : _obj4; + if (_key5 == null) break; if (identical(_key5, k)) return _obj5 == null ? _obj5 = _new(_pKeys5, _factory5, d) : _obj5; + if (_key6 == null) break; if (identical(_key6, k)) return _obj6 == null ? _obj6 = _new(_pKeys6, _factory6, d) : _obj6; + if (_key7 == null) break; if (identical(_key7, k)) return _obj7 == null ? _obj7 = _new(_pKeys7, _factory7, d) : _obj7; + if (_key8 == null) break; if (identical(_key8, k)) return _obj8 == null ? _obj8 = _new(_pKeys8, _factory8, d) : _obj8; + if (_key9 == null) break; if (identical(_key9, k)) return _obj9 == null ? _obj9 = _new(_pKeys9, _factory9, d) : _obj9; + } while (false); + switch (visType) { + case VISIBILITY_LOCAL: return appInjector.getByKey(k, d); + case VISIBILITY_DIRECT_CHILD: return parent._getDirectiveByKey(k, VISIBILITY_LOCAL, d, i); + case VISIBILITY_CHILDREN: return parent._getDirectiveByKey(k, VISIBILITY_CHILDREN, d, i); + // SHADOW + case VISIBILITY_COMPONENT_LOCAL: return parent._getDirectiveByKey(k, VISIBILITY_LOCAL, d, i); + case VISIBILITY_COMPONENT_DIRECT_CHILD: return parent._getDirectiveByKey(k, VISIBILITY_DIRECT_CHILD, d, i); + case VISIBILITY_COMPONENT_CHILDREN: return parent._getDirectiveByKey(k, VISIBILITY_CHILDREN, d, 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, int depth) { + var oldTag = _TAG_GET.makeCurrent(); + int size = paramKeys.length; + int d1 = depth + 1; + var obj; + if (size > 15) { + var params = new List(paramKeys.length); + for(var i = 0; i < paramKeys.length; i++) { + params[i] = _getByKey(paramKeys[i], depth + 1); + } + _TAG_INSTANTIATE.makeCurrent(); + obj = Function.apply(fn, params); + } else { + var a01 = size >= 01 ? _getByKey(paramKeys[00], d1) : null; + var a02 = size >= 02 ? _getByKey(paramKeys[01], d1) : null; + var a03 = size >= 03 ? _getByKey(paramKeys[02], d1) : null; + var a04 = size >= 04 ? _getByKey(paramKeys[03], d1) : null; + var a05 = size >= 05 ? _getByKey(paramKeys[04], d1) : null; + var a06 = size >= 06 ? _getByKey(paramKeys[05], d1) : null; + var a07 = size >= 07 ? _getByKey(paramKeys[06], d1) : null; + var a08 = size >= 08 ? _getByKey(paramKeys[07], d1) : null; + var a09 = size >= 09 ? _getByKey(paramKeys[08], d1) : null; + var a10 = size >= 10 ? _getByKey(paramKeys[09], d1) : null; + var a11 = size >= 11 ? _getByKey(paramKeys[10], d1) : null; + var a12 = size >= 12 ? _getByKey(paramKeys[11], d1) : null; + var a13 = size >= 13 ? _getByKey(paramKeys[12], d1) : null; + var a14 = size >= 14 ? _getByKey(paramKeys[13], d1) : null; + var a15 = size >= 15 ? _getByKey(paramKeys[14], d1) : 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, int d, Injector i) => + super._getDirectiveByKey(k, visType + VISIBILITY_COMPONENT_OFFSET, d, 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, [int depth = 0]) => appInjector.getByKey(key, depth); + _getDirectiveByKey(Key key, int visType, int depth, Injector i) => + parent == null ? i.getByKey(key, depth) : parent._getDirectiveByKey(key, visType, depth, 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 d1728f909..12a195c5a 100644 --- a/lib/core_dom/directive_map.dart +++ b/lib/core_dom/directive_map.dart @@ -1,18 +1,48 @@ part of angular.core.dom_internal; +class DirectiveTypeTuple { + final Directive directive; + final Type type; + DirectiveTypeTuple(this.directive, this.type); + toString() => '@$directive#$type'; +} + @Injectable() -class DirectiveMap extends AnnotationsMap { +class DirectiveMap { + final Map> map = new HashMap>(); DirectiveSelectorFactory _directiveSelectorFactory; FormatterMap _formatters; DirectiveSelector _selector; + + DirectiveMap(Injector injector, + this._formatters, + MetadataExtractor metadataExtractor, + this._directiveSelectorFactory) { + (injector as ModuleInjector).types.forEach((type) { + metadataExtractor(type) + .where((annotation) => annotation is Directive) + .forEach((Directive directive) { + map.putIfAbsent(directive.selector, () => []).add(new DirectiveTypeTuple(directive, type)); + }); + }); + } + DirectiveSelector get selector { if (_selector != null) return _selector; return _selector = _directiveSelectorFactory.selector(this, _formatters); } - DirectiveMap(Injector injector, - this._formatters, - MetadataExtractor metadataExtractor, - this._directiveSelectorFactory) - : super(injector, metadataExtractor); + List operator[](String key) { + var value = map[key]; + if (value == null) throw 'No Directive selector $key found!'; + return value; + } + + void forEach(fn(K, Type)) { + map.forEach((_, types) { + types.forEach((tuple) { + fn(tuple.directive, tuple.type); + }); + }); + } } diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart index 2f3420127..6af0eabfe 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, toFactory: (_) => - new ViewPort(node, parentInjector.getByKey(ANIMATE_KEY))) - ..bindByKey(VIEW_FACTORY_KEY, toValue: templateViewFactory) - ..bindByKey(BOUND_VIEW_FACTORY_KEY, toFactory: (Injector injector) => - templateViewFactory.bind(injector)); - } } @@ -130,7 +120,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) { @@ -196,15 +186,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(() { @@ -231,95 +221,71 @@ 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, toFactory: (Injector injector) { - return new TextMustache(node, ref.valueAST, injector.getByKey(SCOPE_KEY)); - }); - } else if (ref.type == AttrMustache) { - if (nodesAttrsDirectives.isEmpty) { - nodeModule.bind(AttrMustache, toFactory: (Injector injector) { - var scope = injector.getByKey(SCOPE_KEY); - 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, 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, 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); + _createDirectiveFactories(ref, nodeInjector, node, nodeAttrs); if (ref.annotation.module != null) { - nodeModule.install(ref.annotation.module()); + DirectiveBinderFn config = ref.annotation.module; + if (config != null) config(nodeInjector); } - }); - - _registerViewFactory(node, parentInjector, nodeModule); + } - nodeInjector = parentInjector.createChild([nodeModule]); if (_config.elementProbeEnabled) { - probe = _expando[node] = - new ElementProbe(parentInjector.getByKey(ELEMENT_PROBE_KEY), - node, nodeInjector, scope); - 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 5874061e1..cdd0fdbc9 100644 --- a/lib/core_dom/module_internal.dart +++ b/lib/core_dom/module_internal.dart @@ -11,11 +11,12 @@ 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; 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'; @@ -57,11 +58,11 @@ class CoreDomModule extends Module { bind(ElementProbe, toValue: null); // Default to a unlimited-sized TemplateCache - bind(TemplateCache, toFactory: (i) { + bind(TemplateCache, toFactory: (CacheRegister register) { var templateCache = new TemplateCache(); - i.getByKey(CACHE_REGISTER_KEY).registerCache("TemplateCache", templateCache); + register.registerCache("TemplateCache", templateCache); return templateCache; - }); + }, inject: [CACHE_REGISTER_KEY]); bind(dom.NodeTreeSanitizer, toImplementation: NullTreeSanitizer); bind(TextMustache); @@ -70,7 +71,7 @@ class CoreDomModule extends Module { bind(Compiler, toImplementation: TaggingCompiler); bind(CompilerConfig); - bind(ComponentFactory, toFactory: (i) => i.getByKey(SHADOW_DOM_COMPONENT_FACTORY_KEY)); + bind(ComponentFactory, inject: [SHADOW_DOM_COMPONENT_FACTORY_KEY]); bind(ShadowDomComponentFactory); bind(TranscludingComponentFactory); bind(Content); diff --git a/lib/core_dom/selector.dart b/lib/core_dom/selector.dart index d6a19d976..7794694dc 100644 --- a/lib/core_dom/selector.dart +++ b/lib/core_dom/selector.dart @@ -41,9 +41,9 @@ class DirectiveSelector { } if ((match = _CONTAINS_REGEXP.firstMatch(selector)) != null) { - textSelector.add(new _ContainsSelector(annotation, match[1])); + textSelector.add(new _ContainsSelector(selector, match[1])); } else if ((match = _ATTR_CONTAINS_REGEXP.firstMatch(selector)) != null) { - attrSelector.add(new _ContainsSelector(annotation, match[1])); + attrSelector.add(new _ContainsSelector(selector, match[1])); } else if ((selectorParts = _splitCss(selector, type)) != null){ elementSelector.addDirective(selectorParts, new _Directive(type, annotation)); } else { @@ -97,12 +97,12 @@ class DirectiveSelector { // this directive is matched on any attribute name, and so // we need to pass the name to the directive by prefixing it to // the value. Yes it is a bit of a hack. - _directives[selectorRegExp.annotation].forEach((type) { + _directives[selectorRegExp.selector].forEach((DirectiveTypeTuple tuple) { // Pre-compute the AST to watch this value. String expression = _interpolate(value); AST valueAST = _astParser(expression, formatters: _formatters); builder.addDirective(new DirectiveRef( - node, type, selectorRegExp.annotation, new Key(type), attrName, valueAST)); + node, tuple.type, tuple.directive, new Key(tuple.type), attrName, valueAST)); }); } } @@ -135,13 +135,13 @@ class DirectiveSelector { for (var k = 0; k < textSelector.length; k++) { var selectorRegExp = textSelector[k]; if (selectorRegExp.regexp.hasMatch(value)) { - _directives[selectorRegExp.annotation].forEach((type) { + _directives[selectorRegExp.selector].forEach((tuple) { // Pre-compute the AST to watch this value. String expression = _interpolate(value); var valueAST = _astParser(expression, formatters: _formatters); - builder.addDirective(new DirectiveRef(node, type, - selectorRegExp.annotation, new Key(type), value, valueAST)); + builder.addDirective(new DirectiveRef(node, tuple.type, + tuple.directive, new Key(tuple.type), value, valueAST)); }); } } @@ -187,10 +187,10 @@ class _Directive { } class _ContainsSelector { - final Directive annotation; + final String selector; final RegExp regexp; - _ContainsSelector(this.annotation, String regexp) + _ContainsSelector(this.selector, String regexp) : regexp = new RegExp(regexp); } diff --git a/lib/core_dom/shadow_dom_component_factory.dart b/lib/core_dom/shadow_dom_component_factory.dart index 480db034a..acd877b97 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({'SHADOW': true}); // 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, 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..7ca59f1b8 100644 --- a/lib/core_dom/tagging_view_factory.dart +++ b/lib/core_dom/tagging_view_factory.dart @@ -51,46 +51,61 @@ 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 { + elementInjector = binder.bind(view, scope, parentInjector, boundNode, eventHandler, animate); + // TODO(misko): Remove this after we remove controllers. No controllers -> 1to1 Scope:View. + 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 +125,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 +134,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 85d544e60..d4c218aa6 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({'SHADOW': true}); - 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, 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 2d5e382d6..3b78e66f7 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,10 @@ 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; ElementProbe(this.parent, this.element, this.injector, this.scope); } diff --git a/lib/directive/module.dart b/lib/directive/module.dart index b0accbf27..d6a6d5159 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,12 +64,14 @@ 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); bind(NgBindTemplate, toValue: null); bind(NgBindHtml, toValue: null); - bind(dom.NodeValidator, toFactory: (_) => new dom.NodeValidatorBuilder.common()); + bind(dom.NodeValidator, toFactory: () => new dom.NodeValidatorBuilder.common()); bind(NgClass, toValue: null); bind(NgClassOdd, toValue: null); bind(NgClassEven, 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 f86237009..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, toFactory: (i) => i.getByKey(NG_FORM_KEY)); - 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 2ce09b0e8..c68b693c3 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) : super(element, injector, animate) { @@ -599,8 +599,9 @@ class NgBindTypeForDateLike { @Decorator(selector: 'input[type=week][ng-model]', module: InputDateLike.moduleFactory) class InputDateLike { - static Module moduleFactory() => new Module()..bind(NgBindTypeForDateLike, - toFactory: (Injector i) => new NgBindTypeForDateLike(i.getByKey(ELEMENT_KEY))); + 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,8 +695,7 @@ final _uidCounter = new _UidCounter(); @Decorator(selector: 'input[type=radio][ng-model][ng-value]') @Decorator(selector: 'option[ng-value]') 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; @@ -777,7 +777,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 194316c2f..bdec4298b 100644 --- a/lib/directive/ng_repeat.dart +++ b/lib/directive/ng_repeat.dart @@ -136,7 +136,6 @@ class NgRepeat { _onCollectionChange(changes); } else if (_rows != null) { _rows.forEach((row) { - row.scope.destroy(); _viewPort.remove(row.view); }); _rows = null; @@ -172,7 +171,6 @@ class NgRepeat { removeFn((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 7293dd45d..960181b71 100644 --- a/lib/introspection.dart +++ b/lib/introspection.dart @@ -8,6 +8,7 @@ import 'package:di/di.dart'; import 'package:angular/introspection_js.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; /** * Return the [ElementProbe] object for the closest [Element] in the hierarchy. @@ -47,7 +48,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; /** diff --git a/lib/introspection_js.dart b/lib/introspection_js.dart index b3b99f867..87ed9e7c3 100644 --- a/lib/introspection_js.dart +++ b/lib/introspection_js.dart @@ -38,7 +38,7 @@ js.JsObject _jsProbe(ElementProbe probe) { })..['_dart_'] = probe; } -js.JsObject _jsInjector(Injector injector) => +js.JsObject _jsInjector(injector) => new js.JsObject.jsify({"get": injector.get})..['_dart_'] = injector; js.JsObject _jsScope(Scope scope, ScopeStatsConfig config) { diff --git a/lib/metadata.dart b/lib/metadata.dart deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/mock/module.dart b/lib/mock/module.dart index ea3407337..e56387610 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,8 +63,8 @@ class AngularMockModule extends Module { bind(MockHttpBackend); bind(Element, toValue: document.body); bind(Node, toValue: document.body); - bind(HttpBackend, toFactory: (Injector i) => i.getByKey(MOCK_HTTP_BACKEND_KEY)); - bind(VmTurnZone, toFactory: (_) { + 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..f68d34098 100644 --- a/lib/mock/probe.dart +++ b/lib/mock/probe.dart @@ -10,13 +10,15 @@ part of angular.mock; * rootScope.myProbe.directive(SomeAttrDirective); */ @Decorator(selector: '[probe]') +@deprecated class Probe implements DetachAware { final Scope scope; - final Injector injector; + final DirectiveInjector directiveInjector; + final Injector appInjector; final Element element; String _probeName; - Probe(this.scope, this.injector, this.element) { + Probe(this.scope, this.directiveInjector, this.appInjector, this.element) { _probeName = element.attributes['probe']; scope.rootScope.context[_probeName] = this; } @@ -28,6 +30,6 @@ class Probe implements DetachAware { /** * Retrieve a Directive at the current element. */ - directive(Type type) => injector.get(type); + directive(Type type) => directiveInjector.get(type); } 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 33d7b8f2f..a2c004698 100644 --- a/lib/mock/test_injection.dart +++ b/lib/mock/test_injection.dart @@ -3,26 +3,26 @@ 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: (Injector injector) => addModule(new Module())); - moduleInjector = new DynamicInjector(modules: [moduleModule]); + ..bind(Module, toFactory: () => addModule(new Module())); + moduleInjector = new ModuleInjector([moduleModule]); } addModule(module) { if (injector != null) { - throw ["Injector already crated, can not add more modules."]; + throw ["Injector already created, can not add more modules."]; } modules.add(module); return 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,25 @@ 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); + try { + return injector.getByKey(key); + } on NoProviderError catch (e) { + (e as NoProviderError).appendKey(key); + rethrow; + } + }).toList(); + + return cm.apply(args).reflectee; + } } /** diff --git a/lib/playback/playback_http.dart b/lib/playback/playback_http.dart index 7efe1d2f6..4872f0c62 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/di.dart' show Injectable; 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 3d497ae32..9e479dab6 100644 --- a/lib/routing/module.dart +++ b/lib/routing/module.dart @@ -142,10 +142,10 @@ part 'ng_bind_route.dart'; class RoutingModule extends Module { RoutingModule({bool usePushState: true}) { bind(NgRoutingUsePushState); - bind(Router, toFactory: (injector) { - var useFragment = !injector.getByKey(NG_ROUTING_USE_PUSH_STATE_KEY).usePushState; - return new Router(useFragment: useFragment, windowImpl: injector.getByKey(WINDOW_KEY)); - }); + bind(Router, toFactory: (NgRoutingUsePushState state, Window window) { + var useFragment = !state.usePushState; + return new Router(useFragment: useFragment, windowImpl: window); + }, inject: [NG_ROUTING_USE_PUSH_STATE_KEY, WINDOW_KEY]); bind(NgRoutingHelper); bind(RouteProvider, toValue: null); bind(RouteInitializer, toValue: null); diff --git a/lib/routing/ng_bind_route.dart b/lib/routing/ng_bind_route.dart index 935a65ee2..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, toFactory: (i) => i.getByKey(NG_BIND_ROUTE_KEY), - 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 ded1bfb4b..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, toFactory: (i) => i.getByKey(NG_VIEW_KEY)); - - 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 150842664..536c773d0 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/tools/parser_getter_setter/generator.dart'; diff --git a/lib/tools/transformer/expression_generator.dart b/lib/tools/transformer/expression_generator.dart index e3456eccb..aeeb5f8ea 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/di_dynamic.dart' as di; import 'package:path/path.dart' as path; /** @@ -28,6 +30,7 @@ class ExpressionGenerator extends Transformer with ResolverTransformer { ExpressionGenerator(this.options, Resolvers resolvers) { this.resolvers = resolvers; + di.setupModuleTypeReflector(); } Future applyResolver(Transform transform, Resolver resolver) { @@ -46,10 +49,12 @@ class ExpressionGenerator extends Transformer with ResolverTransformer { .forEach(htmlExtractor.parseHtml) .then((_) { var module = new Module() - ..bind(Parser, toFactory: (i) => i.get(DynamicParser)) - ..bind(ParserBackend, toFactory: (i) => i.get(DartGetterSetterGen)); - var injector = - new DynamicInjector(modules: [module], allowImplicitInjection: true); + ..install(new CacheModule()) + ..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 3cdc8c6dd..f87d21c54 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,7 +24,7 @@ packages: code_transformers: description: code_transformers source: hosted - version: "0.1.3+1" + version: "0.1.4+2" collection: description: collection source: hosted @@ -32,7 +32,7 @@ packages: di: description: di source: hosted - version: "1.0.0" + version: "2.0.0-alpha.7" guinness: description: guinness source: hosted @@ -44,7 +44,7 @@ packages: intl: description: intl source: hosted - version: "0.9.9" + version: "0.9.10" logging: description: logging source: hosted diff --git a/pubspec.yaml b/pubspec.yaml index fe4f30bc1..f46bf77cb 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.0.0 <2.0.0' + di: '>=2.0.0-alpha.7' 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..0a6c1a1b1 100644 --- a/test/_specs.dart +++ b/test/_specs.dart @@ -2,6 +2,7 @@ library ng_specs; import 'dart:html' hide Animation; +import 'package:di/di_dynamic.dart'; import 'package:angular/angular.dart'; import 'package:angular/mock/module.dart'; @@ -14,7 +15,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'; @@ -115,6 +115,7 @@ _removeNgBinding(node) { } main() { + setupModuleTypeReflector(); gns.beforeEach(setUpInjector, priority:3); gns.afterEach(tearDownInjector); diff --git a/test/angular_spec.dart b/test/angular_spec.dart index 4ad13f144..efe6686bd 100644 --- a/test/angular_spec.dart +++ b/test/angular_spec.dart @@ -104,13 +104,15 @@ main() { "angular.core.annotation_src.DetachAware", "angular.core.annotation_src.Directive", "angular.core.annotation_src.DirectiveAnnotation", + "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", @@ -230,6 +232,7 @@ main() { "angular.introspection.ngProbe", "angular.introspection.ngQuery", "angular.introspection.ngScope", + "angular.node_injector.DirectiveInjector", "angular.routing.NgBindRoute", "angular.routing.ngRoute", "angular.routing.NgRouteCfg", @@ -246,15 +249,21 @@ main() { "angular.watch_group.Watch", "change_detection.AvgStopwatch", "change_detection.FieldGetterFactory", - "di.CircularDependencyError", - "di.FactoryFn", - "di.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", @@ -262,10 +271,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/core_directive_spec.dart b/test/core/core_directive_spec.dart index ab4cd3149..12632dc14 100644 --- a/test/core/core_directive_spec.dart +++ b/test/core/core_directive_spec.dart @@ -11,11 +11,11 @@ void main() { }); it('should extract attr map from annotated component', (DirectiveMap directives) { - var annotations = directives.annotationsFor(AnnotatedIoComponent); - expect(annotations.length).toEqual(1); - expect(annotations[0] is Component).toBeTruthy(); + var tuples = directives['annotated-io']; + expect(tuples.length).toEqual(1); + expect(tuples[0].directive is Component).toBeTruthy(); - Component annotation = annotations[0]; + Component annotation = tuples[0].directive; expect(annotation.selector).toEqual('annotated-io'); expect(annotation.visibility).toEqual(Directive.LOCAL_VISIBILITY); expect(annotation.exportExpressions).toEqual(['exportExpressions']); @@ -77,11 +77,11 @@ void main() { }); it("should extract attr map from annotated component which inherits other component", (DirectiveMap directives) { - var annotations = directives.annotationsFor(Sub); - expect(annotations.length).toEqual(1); - expect(annotations[0] is Directive).toBeTruthy(); + var tupls = directives['[sub]']; + expect(tupls.length).toEqual(1); + expect(tupls[0].directive is Directive).toBeTruthy(); - Directive annotation = annotations[0]; + Directive annotation = tupls[0].directive; expect(annotation.selector).toEqual('[sub]'); expect(annotation.map).toEqual({ "foo": "=>foo", @@ -112,7 +112,7 @@ class NullParser implements Parser { 'foo': '=>foo' }) class AnnotatedIoComponent { - static module() => new Module()..bind(String, toFactory: (i) => i.get(AnnotatedIoComponent), + static module(i) => i.bind(String, toFactory: (i) => i.get(AnnotatedIoComponent), visibility: Directive.LOCAL_VISIBILITY); AnnotatedIoComponent(Scope scope) { diff --git a/test/core/parser/generated_getter_setter_spec.dart b/test/core/parser/generated_getter_setter_spec.dart index c7004ab4e..5e44e8126 100644 --- a/test/core/parser/generated_getter_setter_spec.dart +++ b/test/core/parser/generated_getter_setter_spec.dart @@ -7,7 +7,7 @@ import 'generated_getter_setter.dart' as gen; main() { describe('hybrid getter-setter', () { beforeEachModule((Module module) { - module..bind(Parser, toFactory: (i) => i.get(DynamicParser)) + module..bind(Parser, inject: [DynamicParser]) ..bind(ClosureMap, toValue: gen.closureMap); }); parser_spec.main(); diff --git a/test/core/parser/parser_spec.dart b/test/core/parser/parser_spec.dart index 5d9b8b3f2..cf6c8a029 100644 --- a/test/core/parser/parser_spec.dart +++ b/test/core/parser/parser_spec.dart @@ -456,7 +456,7 @@ main() { }); xdescribe('reserved words', () { - iit('should support reserved words in member get access', () { + it('should support reserved words in member get access', () { for (String reserved in RESERVED_WORDS) { expect(parser("o.$reserved").eval({ 'o': new Object() })).toEqual(null); expect(parser("o.$reserved").eval({ 'o': { reserved: reserved }})).toEqual(reserved); @@ -1139,10 +1139,10 @@ main() { it('should parse formatters', () { expect(() { eval("1|nonexistent"); - }).toThrow('No Formatter: nonexistent found!'); + }).toThrow('No formatter \'nonexistent\' found!'); expect(() { eval("1|nonexistent", formatters); - }).toThrow('No Formatter: nonexistent found!'); + }).toThrow('No formatter \'nonexistent\' found!'); context['offset'] = 3; expect(eval("'abcd'|substring:1:offset")).toEqual("bc"); @@ -1153,12 +1153,12 @@ main() { var expression = parser("'World'|hello"); expect(() { expression.eval({}, formatters); - }).toThrow('No Formatter: hello found!'); + }).toThrow('No formatter \'hello\' found!'); var module = new Module() + ..bind(FormatterMap) ..bind(HelloFormatter); - var childInjector = injector.createChild([module], - forceNewInstances: [FormatterMap]); + var childInjector = injector.createChild([module]); var newFormatters = childInjector.get(FormatterMap); expect(expression.eval({}, newFormatters)).toEqual('Hello, World!'); diff --git a/test/core/registry_spec.dart b/test/core/registry_spec.dart deleted file mode 100644 index 1a00d2af0..000000000 --- a/test/core/registry_spec.dart +++ /dev/null @@ -1,64 +0,0 @@ -library registry_spec; - -import '../_specs.dart'; -import 'package:angular/application_factory.dart'; - -main() { - describe('RegistryMap', () { - it('should allow for multiple registry keys to be added', () { - var module = new Module() - ..bind(MyMap) - ..bind(A1) - ..bind(A2); - - var injector = applicationFactory().addModule(module).createInjector(); - expect(() { - injector.get(MyMap); - }).not.toThrow(); - }); - - it('should iterate over all types', () { - var module = new Module() - ..bind(MyMap) - ..bind(A1); - - var injector = applicationFactory().addModule(module).createInjector(); - var keys = []; - var types = []; - var map = injector.get(MyMap); - map.forEach((k, t) { keys.add(k); types.add(t); }); - expect(keys).toEqual([new MyAnnotation('A'), new MyAnnotation('B')]); - expect(types).toEqual([A1, A1]); - }); - - it('should safely ignore typedefs', () { - var module = new Module() - ..bind(MyMap) - ..bind(MyTypedef, toValue: (String _) => null); - - var injector = applicationFactory().addModule(module).createInjector(); - expect(() => injector.get(MyMap), isNot(throws)); - }); - }); -} - -typedef void MyTypedef(String arg); - -class MyMap extends AnnotationMap { - MyMap(Injector injector, MetadataExtractor metadataExtractor) - : super(injector, metadataExtractor); -} - - -class MyAnnotation { - final String name; - - const MyAnnotation(String this.name); - - toString() => name; - get hashCode => name.hashCode; - operator==(other) => this.name == other.name; -} - -@MyAnnotation('A') @MyAnnotation('B') class A1 {} -@MyAnnotation('A') class A2 {} 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 97c19d325..542ad3457 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) { @@ -85,7 +86,9 @@ void main() { ..bind(TwoOfTwoDirectives) ..bind(MyController) ..bind(MyParentController) - ..bind(MyChildController); + ..bind(MyChildController) + ..bind(SameNameDecorator) + ..bind(SameNameTransclude); }); beforeEach(inject((TestBed tb) => _ = tb)); @@ -374,7 +377,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; @@ -629,8 +632,8 @@ void main() { it('should expose PublishModuleDirectiveSuperType as PublishModuleDirectiveSuperType', () { _.compile(r'
'); - var probe = _.rootScope.context['publishModuleProbe']; - var directive = probe.injector.get(PublishModuleDirectiveSuperType); + Probe probe = _.rootScope.context['publishModuleProbe']; + var directive = probe.directiveInjector.get(PublishModuleDirectiveSuperType); expect(directive is PublishModuleAttrDirective).toBeTruthy(); }); @@ -658,7 +661,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']); @@ -690,7 +693,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(); @@ -722,8 +725,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(() { @@ -734,8 +738,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); @@ -876,7 +881,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(); @@ -915,8 +937,19 @@ 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'); }); + + it('should allow multiple directives with the same selector of different type', (DirectiveMap map) { + _.compile('
'); + _.rootScope.apply(); + SameNameTransclude transclude = _.rootScope.context['sameTransclude']; + SameNameDecorator decorator = _.rootScope.context['sameDecorator']; + + expect(transclude.valueTransclude).toEqual('worked'); + expect(decorator.valueDecorator).toEqual('worked'); + }); }); })); } @@ -974,7 +1007,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(() { @@ -1023,12 +1057,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; } } @@ -1328,3 +1360,27 @@ class OneTimeDecorator { OneTimeDecorator(this.log); set value(v) => log(v); } + +@Decorator( + selector: '[same-name]', + children: Directive.TRANSCLUDE_CHILDREN, + map: const { '.': '@valueTransclude' } +) +class SameNameTransclude { + var valueTransclude; + SameNameTransclude(ViewPort port, ViewFactory factory, RootScope scope) { + port.insertNew(factory); + scope.context['sameTransclude'] = this; + } +} + +@Decorator( + selector: '[same-name]', + map: const { 'same-name': '@valueDecorator' } +) +class SameNameDecorator { + var valueDecorator; + SameNameDecorator(RootScope scope) { + scope.context['sameDecorator'] = 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/selector_spec.dart b/test/core_dom/selector_spec.dart index 35c8785a9..524359a4c 100644 --- a/test/core_dom/selector_spec.dart +++ b/test/core_dom/selector_spec.dart @@ -2,34 +2,47 @@ library angular.dom.selector_spec; import '../_specs.dart'; -@Decorator(selector:'b') class _BElement{} -@Decorator(selector:'.b') class _BClass{} -@Decorator(selector:'[directive]') class _DirectiveAttr{} -@Decorator(selector:'[wildcard-*]') class _WildcardDirectiveAttr{} -@Decorator(selector:'[directive=d][foo=f]') class _DirectiveFooAttr{} -@Decorator(selector:'b[directive]') class _BElementDirectiveAttr{} -@Decorator(selector:'[directive=value]') class _DirectiveValueAttr{} -@Decorator(selector:'b[directive=value]') class _BElementDirectiveValue{} -@Decorator(selector:':contains(/abc/)') class _ContainsAbc{} -@Decorator(selector:'[*=/xyz/]') class _AttributeContainsXyz{} - -@Component(selector:'component') class _Component{} -@Decorator(selector:'[attribute]') class _Attribute{} -@Decorator(selector:'[structural]', - children: Directive.TRANSCLUDE_CHILDREN) - class _Structural{} - -@Decorator(selector:'[ignore-children]', - children: Directive.IGNORE_CHILDREN) - class _IgnoreChildren{} - -@Decorator(selector: '[my-model][required]') -@Decorator(selector: '[my-model][my-required]') - class _TwoDirectives {} - -@Decorator(selector: '[two-directives]') class _OneOfTwoDirectives {} -@Decorator(selector: '[two-directives]') class _TwoOfTwoDirectives {} - +const _aBElement = const Decorator(selector:'b'); +const _aBClass = const Decorator(selector:'.b'); +const _aDirectiveAttr = const Decorator(selector:'[directive]'); +const _aWildcardDirectiveAttr = const Decorator(selector:'[wildcard-*]'); +const _aDirectiveFooAttr = const Decorator(selector:'[directive=d][foo=f]'); +const _aBElementDirectiveAttr = const Decorator(selector:'b[directive]'); +const _aDirectiveValueAttr = const Decorator(selector:'[directive=value]'); +const _aBElementDirectiveValue = const Decorator(selector:'b[directive=value]'); +const _aContainsAbc = const Decorator(selector:':contains(/abc/)'); +const _aAttributeContainsXyz = const Decorator(selector:'[*=/xyz/]'); +const _aAttribute = const Decorator(selector:'[attribute]'); +const _aCComponent = const Component(selector:'component'); +const _aStructural = const Decorator(selector:'[structural]', + children: Directive.TRANSCLUDE_CHILDREN); +const _aIgnoreChildren = const Decorator(selector:'[ignore-children]', + children: Directive.IGNORE_CHILDREN); +const _aTwoDirectives0 = const Decorator(selector: '[my-model][required]'); +const _aTwoDirectives1 = const Decorator(selector: '[my-model][my-required]'); +const _aOneOfTwoDirectives = const Decorator(selector: '[two-directives]'); +const _aTwoOfTwoDirectives = const Decorator(selector: '[two-directives]'); + + +@_aBElement class _BElement{} +@_aBClass class _BClass{} +@_aDirectiveAttr class _DirectiveAttr{} +@_aWildcardDirectiveAttr class _WildcardDirectiveAttr{} +@_aDirectiveFooAttr class _DirectiveFooAttr{} +@_aBElementDirectiveAttr class _BElementDirectiveAttr{} +@_aDirectiveValueAttr class _DirectiveValueAttr{} +@_aBElementDirectiveValue class _BElementDirectiveValue{} +@_aContainsAbc class _ContainsAbc{} +@_aAttributeContainsXyz class _AttributeContainsXyz{} +@_aCComponent class _CComponent{} +@_aAttribute class _Attribute{} +@_aStructural class _Structural{} +@_aIgnoreChildren class _IgnoreChildren{} +@_aOneOfTwoDirectives class _OneOfTwoDirectives {} +@_aTwoOfTwoDirectives class _TwoOfTwoDirectives {} + +@_aTwoDirectives0 +@_aTwoDirectives1 class _TwoDirectives {} main() { describe('Selector', () { @@ -51,7 +64,7 @@ main() { ..bind(_BElementDirectiveValue) ..bind(_ContainsAbc) ..bind(_AttributeContainsXyz) - ..bind(_Component) + ..bind(_CComponent) ..bind(_Attribute) ..bind(_Structural) ..bind(_IgnoreChildren) @@ -69,14 +82,14 @@ main() { expect( selector(element = e('')), toEqualsDirectiveInfos([ - { "selector": 'b', "value": null, "element": element} + { "selector": 'b', "value": null, "element": element, "annotation": _aBElement} ])); }); it('should match directive on class', () { expect(selector(element = e('
')), toEqualsDirectiveInfos([ - { "selector": '.b', "value": null, "element": element} + { "selector": '.b', "value": null, "element": element, "annotation": _aBClass} ])); }); @@ -84,39 +97,39 @@ main() { expect(selector(element = e('
')), toEqualsDirectiveInfos([ { "selector": '[directive]', "value": 'abc', "element": element, - "name": 'directive' }])); + "name": 'directive', "annotation": _aDirectiveAttr }])); expect(selector(element = e('
')), toEqualsDirectiveInfos([ { "selector": '[directive]', "value": '', "element": element, - "name": 'directive' }])); + "name": 'directive', "annotation": _aDirectiveAttr }])); }); it('should match directive on element[attribute]', () { expect(selector(element = e('')), toEqualsDirectiveInfos([ - { "selector": 'b', "value": null, "element": element}, - { "selector": '[directive]', "value": 'abc', "element": element}, - { "selector": 'b[directive]', "value": 'abc', "element": element} + { "selector": 'b', "value": null, "element": element, "annotation": _aBElement}, + { "selector": '[directive]', "value": 'abc', "element": element, "annotation": _aDirectiveAttr}, + { "selector": 'b[directive]', "value": 'abc', "element": element, "annotation": _aBElementDirectiveAttr} ])); }); it('should match directive on [attribute=value]', () { expect(selector(element = e('
')), toEqualsDirectiveInfos([ - { "selector": '[directive]', "value": 'value', "element": element}, - { "selector": '[directive=value]', "value": 'value', "element": element} + { "selector": '[directive]', "value": 'value', "element": element, "annotation": _aDirectiveAttr}, + { "selector": '[directive=value]', "value": 'value', "element": element, "annotation": _aDirectiveValueAttr} ])); }); it('should match directive on element[attribute=value]', () { expect(selector(element = e('
')), toEqualsDirectiveInfos([ - { "selector": 'b', "value": null, "element": element, "name": null}, - { "selector": '[directive]', "value": 'value', "element": element}, - { "selector": '[directive=value]', "value": 'value', "element": element}, - { "selector": 'b[directive]', "value": 'value', "element": element}, - { "selector": 'b[directive=value]', "value": 'value', "element": element} + { "selector": 'b', "value": null, "element": element, "name": null, "annotation": _aBElement}, + { "selector": '[directive]', "value": 'value', "element": element, "annotation": _aDirectiveAttr}, + { "selector": '[directive=value]', "value": 'value', "element": element, "annotation": _aDirectiveValueAttr}, + { "selector": 'b[directive]', "value": 'value', "element": element, "annotation": _aBElementDirectiveAttr}, + { "selector": 'b[directive=value]', "value": 'value', "element": element, "annotation": _aBElementDirectiveValue} ])); }); @@ -126,7 +139,9 @@ main() { { "selector": '[*=/xyz/]', "value": 'attr', "ast": '"before-xyz-after"', - "element": element, "name": 'attr'} + "element": element, + "name": 'attr', + "annotation": _aAttributeContainsXyz} ])); }); @@ -134,7 +149,7 @@ main() { expect(selector(element = e('
')), toEqualsDirectiveInfos([ { "selector": '[wildcard-*]', "value": 'ignored', - "element": element, "name": 'wildcard-match'} + "element": element, "name": 'wildcard-match', "annotation": _aWildcardDirectiveAttr} ])); }); @@ -144,23 +159,23 @@ main() { expect(eb, toEqualsDirectiveInfos( null, - template: {"selector": "[structural]", "value": "", "element": element})); + template: {"selector": "[structural]", "value": "", "element": element, "annotation": _aStructural})); expect(eb.templateBinder, toEqualsDirectiveInfos( [ - { "selector": "[attribute]", "value": "", "element": element }, - { "selector": "[ignore-children]", "value": "", "element": element } + { "selector": "[attribute]", "value": "", "element": element, "annotation": _aAttribute }, + { "selector": "[ignore-children]", "value": "", "element": element, "annotation": _aIgnoreChildren } ], - component: { "selector": "component", "value": null, "element": element })); + component: { "selector": "component", "value": null, "element": element, "annotation": _aCComponent })); })); it('should match on multiple directives', () { expect(selector(element = e('
')), toEqualsDirectiveInfos([ - { "selector": '[directive]', "value": 'd', "element": element}, - { "selector": '[directive=d][foo=f]', "value": 'f', "element": element} + { "selector": '[directive]', "value": 'd', "element": element, "annotation": _aDirectiveAttr}, + { "selector": '[directive=d][foo=f]', "value": 'f', "element": element, "annotation": _aDirectiveFooAttr} ])); }); @@ -187,8 +202,8 @@ main() { it('should match an two directives with the same selector', () { expect(selector(element = e('
')), toEqualsDirectiveInfos([ - { "selector": '[two-directives]', "value": '', "element": element}, - { "selector": '[two-directives]', "value": '', "element": element} + { "selector": '[two-directives]', "value": '', "element": element, "annotation": _aOneOfTwoDirectives}, + { "selector": '[two-directives]', "value": '', "element": element, "annotation": _aTwoOfTwoDirectives} ])); }); @@ -252,10 +267,11 @@ class DirectiveInfosMatcher extends Matcher { Description describe(Description description) => description..add(expected.toString()); - bool _refMatches(directiveRef, expectedMap) => + bool _refMatches(DirectiveRef directiveRef, Map expectedMap) => directiveRef.element == expectedMap['element'] && directiveRef.annotation.selector == expectedMap['selector'] && directiveRef.value == expectedMap['value'] && + (expectedMap['annotation'] == null || directiveRef.annotation == expectedMap['annotation']) && (directiveRef.valueAST == null || directiveRef.valueAST.expression == expectedMap['ast']); diff --git a/test/core_dom/view_spec.dart b/test/core_dom/view_spec.dart index 7fae0a95e..b3bdf09d9 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,13 +202,13 @@ 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) ..bind(AFormatter) ..bind(ADirective) - ..bind(Node, toFactory: (injector) => document.body); + ..bind(Node, toFactory: () => document.body); Injector rootInjector = applicationFactory() .addModule(rootModule) @@ -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..276335017 100644 --- a/test/directive/ng_form_spec.dart +++ b/test/directive/ng_form_spec.dart @@ -147,7 +147,7 @@ void main() { _.compile('
'); scope.apply(); - expect(scope.context['formProbe'].injector.get(NgControl) is NgForm).toBeTruthy(); + expect(scope.context['formProbe'].directiveInjector.get(NgControl) is NgForm).toBeTruthy(); }); it('should add and remove the correct flags when set to valid and to invalid', @@ -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 20b87665b..1254a0554 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()); 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()); 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()); dom.querySelector('body').append(element); var input = new InputTextLike(element, model, scope, ngModelOptions); @@ -566,7 +570,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()); dom.querySelector('body').append(element); var input = new InputTextLike(element, model, scope, ngModelOptions); @@ -777,7 +781,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()); 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/formatter/currency_spec.dart b/test/formatter/currency_spec.dart index a488c107f..f747959fa 100644 --- a/test/formatter/currency_spec.dart +++ b/test/formatter/currency_spec.dart @@ -8,7 +8,7 @@ void main() { var currency; beforeEach((FormatterMap map, Injector injector) { - currency = injector.get(map[new Formatter(name: 'currency')]); + currency = injector.get(map['currency']); }); diff --git a/test/formatter/date_spec.dart b/test/formatter/date_spec.dart index 124dcb2e4..f207b1e58 100644 --- a/test/formatter/date_spec.dart +++ b/test/formatter/date_spec.dart @@ -13,7 +13,7 @@ void main() { var date; beforeEach((FormatterMap map, Injector injector) { - date = injector.get(map[new Formatter(name: 'date')]); + date = injector.get(map['date']); }); it('should ignore falsy inputs', () { diff --git a/test/formatter/filter_spec.dart b/test/formatter/filter_spec.dart index 0c614912a..dfc168979 100644 --- a/test/formatter/filter_spec.dart +++ b/test/formatter/filter_spec.dart @@ -52,7 +52,7 @@ main() { var filter; beforeEach((Injector injector, FormatterMap filterMap) { - filter = injector.get(filterMap[new Formatter(name: 'filter')]); + filter = injector.get(filterMap['filter']); }); it('should formatter by string', () { diff --git a/test/formatter/number_spec.dart b/test/formatter/number_spec.dart index cf4889bdd..782418724 100644 --- a/test/formatter/number_spec.dart +++ b/test/formatter/number_spec.dart @@ -8,7 +8,7 @@ void main() { var number; beforeEach((FormatterMap map, Injector injector) { - number = injector.get(map[new Formatter(name: 'number')]); + number = injector.get(map['number']); }); diff --git a/test/introspection_spec.dart b/test/introspection_spec.dart index d8ff35c74..a0ab1cb7e 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_bind_route_spec.dart b/test/routing/ng_bind_route_spec.dart index a3351b863..4de361806 100644 --- a/test/routing/ng_bind_route_spec.dart +++ b/test/routing/ng_bind_route_spec.dart @@ -20,14 +20,14 @@ main() { it('should inject null RouteProvider when no ng-bind-route', async(() { Element root = _.compile('
    '); - expect(_.rootScope.context['routeProbe'].injector.get(RouteProvider)).toBeNull(); + expect(_.rootScope.context['routeProbe'].directiveInjector.get(RouteProvider)).toBeNull(); })); it('should inject RouteProvider with correct flat route', async(() { Element root = _.compile( '
    '); - expect(_.rootScope.context['routeProbe'].injector.get(RouteProvider).routeName) + expect(_.rootScope.context['routeProbe'].directiveInjector.get(RouteProvider).routeName) .toEqual('library'); })); @@ -39,14 +39,14 @@ main() { '
    ' '
' '
'); - expect(_.rootScope.context['routeProbe'].injector.get(RouteProvider).route.name) + expect(_.rootScope.context['routeProbe'].directiveInjector.get(RouteProvider).route.name) .toEqual('all'); })); it('should expose NgBindRoute as RouteProvider', async(() { Element root = _.compile( '
'); - expect(_.rootScope.context['routeProbe'].injector.get(RouteProvider) is NgBindRoute).toBeTruthy(); + expect(_.rootScope.context['routeProbe'].directiveInjector.get(RouteProvider) is NgBindRoute).toBeTruthy(); })); }); diff --git a/test/routing/ng_view_spec.dart b/test/routing/ng_view_spec.dart index 1709ff4f8..e38ae3717 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.directiveInjector.get(RouteProvider) is NgView).toBeTruthy(); })); diff --git a/test/routing/routing_spec.dart b/test/routing/routing_spec.dart index bbc63cd31..993a1293b 100644 --- a/test/routing/routing_spec.dart +++ b/test/routing/routing_spec.dart @@ -16,7 +16,7 @@ main() { router = new Router(useFragment: false, windowImpl: new MockWindow()); m ..install(new AngularMockModule()) - ..bind(RouteInitializerFn, toFactory: (_) => initRoutes) + ..bind(RouteInitializerFn, toFactory: () => initRoutes) ..bind(Router, toValue: router); }); 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(); } From 81f206ccc632b4c14756b682e0c4fc4794406a29 Mon Sep 17 00:00:00 2001 From: Anting Shen Date: Fri, 11 Jul 2014 09:49:58 -0700 Subject: [PATCH 5/5] fix: changes for updated DI alpha.9 transformers --- benchmark/pubspec.lock | 2 +- benchmark/web/tree.dart | 2 -- bin/parser_generator_for_spec.dart | 2 -- lib/application_factory_static.dart | 1 - lib/cache/module.dart | 3 +++ lib/tools/transformer/expression_generator.dart | 7 +++---- pubspec.yaml | 2 +- test/_specs.dart | 2 -- 8 files changed, 8 insertions(+), 13 deletions(-) diff --git a/benchmark/pubspec.lock b/benchmark/pubspec.lock index 987fc6d0b..70b9a773c 100644 --- a/benchmark/pubspec.lock +++ b/benchmark/pubspec.lock @@ -40,7 +40,7 @@ packages: path: "../../di.dart" relative: true source: path - version: "2.0.0-alpha.6" + version: "2.0.0-alpha.9" html5lib: description: html5lib source: hosted diff --git a/benchmark/web/tree.dart b/benchmark/web/tree.dart index 0fa2e84a8..e382fb38a 100644 --- a/benchmark/web/tree.dart +++ b/benchmark/web/tree.dart @@ -1,5 +1,4 @@ import 'package:di/di.dart'; -import 'package:di/di_dynamic.dart'; import 'package:angular/angular.dart'; import 'package:angular/core_dom/module_internal.dart'; import 'package:angular/application_factory.dart'; @@ -247,7 +246,6 @@ 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 020770b87..e35c98b68 100644 --- a/bin/parser_generator_for_spec.dart +++ b/bin/parser_generator_for_spec.dart @@ -1,14 +1,12 @@ import 'dart:io' as io; import 'package:di/di.dart'; -import 'package:di/di_dynamic.dart'; import 'package:angular/cache/module.dart'; import 'package:angular/core/parser/lexer.dart'; import 'package:angular/core/parser/parser.dart'; import 'package:angular/tools/parser_getter_setter/generator.dart'; main(arguments) { - setupModuleTypeReflector(); Module module = new Module() ..bind(Lexer) ..bind(ParserGetterSetter) diff --git a/lib/application_factory_static.dart b/lib/application_factory_static.dart index 9a85aead2..a44c3f490 100644 --- a/lib/application_factory_static.dart +++ b/lib/application_factory_static.dart @@ -30,7 +30,6 @@ */ library angular.app.factory.static; -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'; diff --git a/lib/cache/module.dart b/lib/cache/module.dart index 51cbfaab4..e7f1f12f8 100644 --- a/lib/cache/module.dart +++ b/lib/cache/module.dart @@ -13,4 +13,7 @@ class CacheModule extends Module { CacheModule() { bind(CacheRegister); } + CacheModule.withReflector(reflector): super.withReflector(reflector) { + bind(CacheRegister); + } } diff --git a/lib/tools/transformer/expression_generator.dart b/lib/tools/transformer/expression_generator.dart index aeeb5f8ea..4bd1fdb74 100644 --- a/lib/tools/transformer/expression_generator.dart +++ b/lib/tools/transformer/expression_generator.dart @@ -14,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/di_dynamic.dart' as di; +import 'package:di/src/reflector_dynamic.dart'; import 'package:path/path.dart' as path; /** @@ -30,7 +30,6 @@ class ExpressionGenerator extends Transformer with ResolverTransformer { ExpressionGenerator(this.options, Resolvers resolvers) { this.resolvers = resolvers; - di.setupModuleTypeReflector(); } Future applyResolver(Transform transform, Resolver resolver) { @@ -48,8 +47,8 @@ class ExpressionGenerator extends Transformer with ResolverTransformer { return _getHtmlSources(transform, resolver) .forEach(htmlExtractor.parseHtml) .then((_) { - var module = new Module() - ..install(new CacheModule()) + var module = new Module.withReflector(getReflector()) + ..install(new CacheModule.withReflector(getReflector())) ..bind(Parser, toImplementation: DynamicParser) ..bind(ParserBackend, toImplementation: DartGetterSetterGen) ..bind(Lexer) diff --git a/pubspec.yaml b/pubspec.yaml index f46bf77cb..03f53db48 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: browser: '>=0.10.0 <0.11.0' code_transformers: '>=0.1.4+2 <0.2.0' collection: '>=0.9.1 <1.0.0' - di: '>=2.0.0-alpha.7' + di: '>=2.0.0-alpha.9' 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 0a6c1a1b1..f6c024973 100644 --- a/test/_specs.dart +++ b/test/_specs.dart @@ -2,7 +2,6 @@ library ng_specs; import 'dart:html' hide Animation; -import 'package:di/di_dynamic.dart'; import 'package:angular/angular.dart'; import 'package:angular/mock/module.dart'; @@ -115,7 +114,6 @@ _removeNgBinding(node) { } main() { - setupModuleTypeReflector(); gns.beforeEach(setUpInjector, priority:3); gns.afterEach(tearDownInjector);