From 6860a001e7e46e906759fb612bfc21f38c2bc7c8 Mon Sep 17 00:00:00 2001 From: Rado Kirov Date: Wed, 27 Aug 2014 15:52:49 -0700 Subject: [PATCH] feat(directives): remove the @Controller directive Completely remove the deprecated Controller directive. Major update to the demos to use setRootContext (where applicable) and to move to Components for the beefier demos (bouncing balls and form). A temporary work around was required in demos (animation and todo) so that rootContext have Map interface. The work arounds will disappear after the context is set to the component. Library changes were pulled out of the first commit in #1269. --- example/pubspec.lock | 40 ++--- example/pubspec.yaml | 6 +- example/web/animation.dart | 16 +- example/web/animation.html | 2 + example/web/bouncing_balls.dart | 30 ++-- example/web/bouncing_balls.html | 61 +------ example/web/bouncing_controller.css | 31 ++++ example/web/bouncing_controller.html | 25 +++ example/web/form.dart | 18 +- example/web/form.html | 170 +----------------- example/web/form_controller.dart | 131 ++++++++++++++ example/web/form_controller.html | 166 +++++++++++++++++ example/web/hello_world.dart | 6 +- example/web/hello_world.html | 6 +- example/web/todo.dart | 33 ++-- example/web/todo.html | 2 +- lib/application.dart | 9 +- lib/application_factory.dart | 1 - lib/application_factory_static.dart | 14 +- lib/core/annotation.dart | 2 +- lib/core/annotation_src.dart | 156 +++++----------- lib/core_dom/element_binder.dart | 6 - .../shadow_dom_component_factory.dart | 6 +- lib/transformer.dart | 1 - scripts/travis/build.sh | 8 +- test/angular_spec.dart | 1 - test/core/annotation_src_spec.dart | 21 --- test/core_dom/compiler_spec.dart | 101 ----------- test/core_dom/event_handler_spec.dart | 41 ++--- test/directive/ng_model_spec.dart | 25 ++- test_e2e/hello_world_spec.dart | 6 +- 31 files changed, 531 insertions(+), 610 deletions(-) create mode 100644 example/web/bouncing_controller.css create mode 100644 example/web/bouncing_controller.html create mode 100644 example/web/form_controller.dart create mode 100644 example/web/form_controller.html diff --git a/example/pubspec.lock b/example/pubspec.lock index 3adeada5d..8379b928e 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -10,7 +10,7 @@ packages: path: ".." relative: true source: path - version: "0.11.0" + version: "0.14.0" args: description: args source: hosted @@ -18,7 +18,7 @@ packages: barback: description: barback source: hosted - version: "0.13.0" + version: "0.14.0+3" browser: description: browser source: hosted @@ -30,11 +30,11 @@ packages: collection: description: collection source: hosted - version: "0.9.2" + version: "0.9.4" di: description: di source: hosted - version: "2.0.1" + version: "2.0.2" html5lib: description: html5lib source: hosted @@ -42,31 +42,27 @@ packages: intl: description: intl source: hosted - version: "0.8.10+4" + version: "0.11.6" logging: description: logging source: hosted - version: "0.9.1+1" + version: "0.9.2" matcher: description: matcher source: hosted - version: "0.10.0" - meta: - description: meta - source: hosted - version: "0.8.8" - mock: - description: mock - source: hosted - version: "0.10.0+1" + version: "0.11.1" path: description: path source: hosted - version: "1.2.1" + version: "1.3.0" perf_api: description: perf_api source: hosted - version: "0.0.8" + version: "0.0.9" + quiver: + description: quiver + source: hosted + version: "0.18.2" route_hierarchical: description: route_hierarchical source: hosted @@ -74,7 +70,7 @@ packages: source_maps: description: source_maps source: hosted - version: "0.9.0" + version: "0.9.4" source_span: description: source_span source: hosted @@ -82,7 +78,7 @@ packages: stack_trace: description: stack_trace source: hosted - version: "0.9.3+1" + version: "0.9.3+2" typed_mock: description: typed_mock source: hosted @@ -90,12 +86,12 @@ packages: unittest: description: unittest source: hosted - version: "0.10.1+2" + version: "0.11.0+5" utf: description: utf source: hosted - version: "0.9.0" + version: "0.9.0+1" web_components: description: web_components source: hosted - version: "0.3.3" + version: "0.6.0+1" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 56d641013..992598a6b 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -5,7 +5,11 @@ dependencies: path: ../ browser: any unittest: any + quiver: any web_components: any transformers: -- angular +- angular: + html_files: + - web/bouncing_controller.html + - web/form_controller.html diff --git a/example/web/animation.dart b/example/web/animation.dart index a60f569d2..15c4fc3a6 100644 --- a/example/web/animation.dart +++ b/example/web/animation.dart @@ -3,20 +3,29 @@ library animation; import 'package:angular/angular.dart'; import 'package:angular/application_factory.dart'; import 'package:angular/animate/module.dart'; +import 'package:quiver/collection.dart'; part 'animation/repeat_demo.dart'; part 'animation/visibility_demo.dart'; part 'animation/stress_demo.dart'; part 'animation/css_demo.dart'; -@Controller( - selector: '[animation-demo]', - publishAs: 'demo') +@Injectable() class AnimationDemo { final pages = ["About", "ng-repeat", "Visibility", "Css", "Stress Test"]; var currentPage = "About"; } +// Temporary workaround, because context needs to extend Map. +@Injectable() +class AnimationDemoHashMap extends DelegatingMap { + final Map _delegate; + AnimationDemoHashMap(AnimationDemo demo) : _delegate = new Map() { + _delegate['demo'] = demo; + } + Map get delegate => _delegate; +} + class AnimationDemoModule extends Module { AnimationDemoModule() { install(new AnimationModule()); @@ -30,5 +39,6 @@ class AnimationDemoModule extends Module { main() { applicationFactory() .addModule(new AnimationDemoModule()) + .rootContextType(AnimationDemoHashMap) .run(); } diff --git a/example/web/animation.html b/example/web/animation.html index d0728b00b..6328ae2c1 100644 --- a/example/web/animation.html +++ b/example/web/animation.html @@ -14,6 +14,7 @@
+

About

The NgAnimate module is a port with modifications of the original AngularJS animation module. The default implementation does nothing. @@ -22,6 +23,7 @@

About

added it allows you define and run css animations on your elements with pure CSS.

Check out the demos above.

+

ng-repeat Demo

diff --git a/example/web/bouncing_balls.dart b/example/web/bouncing_balls.dart index d2d3ca3a4..fc1057462 100644 --- a/example/web/bouncing_balls.dart +++ b/example/web/bouncing_balls.dart @@ -1,9 +1,10 @@ -import 'package:angular/angular.dart'; -import 'package:angular/application_factory.dart'; import 'dart:html'; import 'dart:math'; import 'dart:core'; +import 'package:angular/angular.dart'; +import 'package:angular/application_factory.dart'; + var random = new Random(); var width = 400; var height = 400; @@ -23,23 +24,26 @@ class BallModel { } return color; } - } -@Controller( - selector: '[bounce-controller]', - publishAs: 'bounce') + +@Component( + selector: 'bounce-controller', + publishAs: 'ctrl', + templateUrl: 'bouncing_controller.html', + cssUrl: 'bouncing_controller.css' +) class BounceController { + RootScope rootScope; var lastTime = window.performance.now(); var run = false; var fps = 0; var digestTime = 0; var currentDigestTime = 0; var balls = []; - final Scope scope; var ballClassName = 'ball'; - BounceController(this.scope) { + BounceController(this.rootScope) { changeCount(100); if (run) tick(); } @@ -72,7 +76,7 @@ class BounceController { void timeDigest() { var start = window.performance.now(); digestTime = currentDigestTime; - scope.rootScope.domRead(() { + rootScope.domRead(() { currentDigestTime = window.performance.now() - start; }); } @@ -82,7 +86,7 @@ class BounceController { var delay = now - lastTime; fps = (1000/delay).round(); - for(var i=0, ii=balls.length; i Bouncing balls - -
-
-
-
- -
-
-
-
-
- - {{bounce.fps}} fps. ({{bounce.balls.length}} balls) [{{1000/bounce.fps}} ms]
- Digest: {{bounce.digestTime}} ms
- +1 - +10 - +100 -
- -1 - -10 - -100 -
- ▶❙❙
- Toggle CSS
- noop
-
+ diff --git a/example/web/bouncing_controller.css b/example/web/bouncing_controller.css new file mode 100644 index 000000000..95b61aef6 --- /dev/null +++ b/example/web/bouncing_controller.css @@ -0,0 +1,31 @@ +.balls { + border: 1px solid black; + width: 420px; + height: 420px; + margin: 5px; +} + +.ball { + display: inline-block; + position: absolute; + width: 20px; + height: 20px; + border: 1px solid black; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; +} + +.fps-bar { + width: 200px; + height: 10px; + border: 1px solid black; + display: inline-block; + margin-left: 5px; +} + +.fps { + height: 10px; + width: 60px; + background-color: green; +} diff --git a/example/web/bouncing_controller.html b/example/web/bouncing_controller.html new file mode 100644 index 000000000..04cb46f28 --- /dev/null +++ b/example/web/bouncing_controller.html @@ -0,0 +1,25 @@ +
+
+
+ +
+
+
+
+
+ +{{ ctrl.fps }} fps. ({{ ctrl.balls.length }} balls) [{{ 1000 / ctrl.fps }} ms]
+Digest: {{ ctrl.digestTime }} ms
++1 ++10 ++100 +
+-1 +-10 +-100 +
+▶❙❙
+Toggle CSS
+noop
\ No newline at end of file diff --git a/example/web/form.dart b/example/web/form.dart index 99a6c18c0..4d96eb035 100644 --- a/example/web/form.dart +++ b/example/web/form.dart @@ -1,9 +1,11 @@ import 'package:angular/angular.dart'; import 'package:angular/application_factory.dart'; -@Controller( - selector: '[form-controller]', - publishAs: 'form_ctrl') +@Component( + selector: '[form-controller]', + templateUrl: 'form_controller.html', + publishAs: 'form_ctrl', + useShadowDom: false) class FormCtrl { static const String _COLOR_HEX = "hex"; static const String _COLOR_HSL = "hsl"; @@ -104,11 +106,13 @@ class FormCtrl { } } -@Controller( - selector: '[preview-controller]', - publishAs: 'preview') +@Decorator( + selector: '[preview-controller]' +) class PreviewCtrl { - static const _DEFAULT_COLOR = '#555'; + PreviewCtrl(Scope scope) { + scope.context['preview'] = this; + } List _collection = []; diff --git a/example/web/form.html b/example/web/form.html index e3246f256..74f0cbf5d 100644 --- a/example/web/form.html +++ b/example/web/form.html @@ -9,175 +9,7 @@ - -
-
- -
-

Wallpaper Resolution Size

-
-
- -
-
-
- -
-

Account Details

-
- - -
- -
- - -
- -
- -
- -
You did not enter your age properly
- -
- -
- -
You did not enter any additional info
-
- -
-

Colors

- - New Color - -
-
-
- -
-
-
-
- - - -
-
- - - -
-
-
- -
-
-
- -
-
-
- - - -
- - -
- -
-
-
-
-
- - - -
-

Your data has been successfully submitted...

-
- -
-

There was an error

- -

Account Details & Wallpaper

-
    -
  1. - You did not select a wallpaper resolution size. -
  2. - -
  3. - You did not enter your first name. -
  4. - -
  5. - You did not enter your last name. -
  6. - -
  7. - You left your email address blank. -
  8. -
  9. - You did not enter your email addresscorrectly -
  10. - -
  11. - You did not select your age -
  12. -
  13. - You have selected an invalid age -
  14. - -
  15. - You did not enter what city you live in -
  16. - -
  17. - You did not enter any additional info about yourself -
  18. - -
  19. - You did not input enough text for your additional info -
  20. -
- -
-
-

Colors

-

One or more of the colors within the form is invalid...

-
    -
  1. -
    - You did not set a valid color for one of the color inputs. -
    -
    - You left a color input empty. -
    -
  2. -
-
-
-
-
+
diff --git a/example/web/form_controller.dart b/example/web/form_controller.dart new file mode 100644 index 000000000..6d747aa9f --- /dev/null +++ b/example/web/form_controller.dart @@ -0,0 +1,131 @@ +import 'package:angular/angular.dart'; + +@Component( + selector: '[form-controller]', + templateUrl: 'form_controller.html', + publishAs: 'form_ctrl', + useShadowDom: false) +class FormCtrl { + static const String _COLOR_HEX = "hex"; + static const String _COLOR_HSL = "hsl"; + static const String _COLOR_RGB = "rgb"; + static const String _COLOR_NAME = "name"; + + static const _COLOR_TYPES = const [_COLOR_RGB, _COLOR_HSL, _COLOR_HEX, _COLOR_NAME]; + + static const _RESOLUTIONS = const ['1024x600', + '1280x800', + '1366x768', + '1440x900', + '1600x900', + '1680x1050', + '1920x1080', + '1920x1200', + '2560x1440', + '2560x1600']; + + final Scope scope; + final NgForm form; + final List colors = []; + final List formattedColors = []; + + FormCtrl(this.scope, this.form) { + newColor(_COLOR_HEX, '#222'); + newColor(_COLOR_HEX, '#444'); + newColor(_COLOR_HEX, '#000'); + } + + List get colorTypes => _COLOR_TYPES; + + List get resolutions => _RESOLUTIONS; + + void submit() { + form.reset(); + } + + int getTotalSquaresFromInput() => getTotalSquares(scope.context['squares']); + + int getTotalSquares(inputValue) { + var value = 4; + if(inputValue != null) { + try { + value = double.parse(inputValue.toString()); + } catch(e) { + } + } + return (value * value).toInt(); + } + + List formatColors() { + formattedColors.clear(); + colors.forEach((color) { + var value = null; + switch(color['type']) { + case _COLOR_HEX: + value = color['hex']; + break; + case _COLOR_HSL: + var hue = color['hue']; + var saturation = color['saturation']; + var luminance = color['luminance']; + if(hue != null && saturation != null && luminance != null) { + value = "hsl($hue, $saturation%, $luminance%)"; + } + break; + case _COLOR_RGB: + var red = color['red']; + var blue = color['blue']; + var green = color['green']; + if(red != null && green != null && blue != null) { + value = "rgb($red, $green, $blue)"; + } + break; + default: //COLOR_NAME + value = color['name']; + break; + } + if(value != null) { + formattedColors.add(value); + } + }); + return formattedColors; + } + + void newColor([String type = _COLOR_HEX, String color]) { + colors.add({ + 'id' : colors.length, + 'type' : type, + 'hex' : type == _COLOR_HEX ? color : '', + 'hue' : '', + 'saturation' : '', + 'luminance' : '', + 'red' : '', + 'green' : '', + 'blue': '', + 'name': '' + }); + } +} + +@Decorator( + selector: '[preview-controller]' +) +class PreviewCtrl { + PreviewCtrl(Scope scope) { + scope.context['preview'] = this; + } + + List _collection = []; + + List expandList(items, limit) { + _collection.clear(); + if(items != null && items.length > 0) { + for (var i = 0; i < limit; i++) { + var x = i % items.length; + _collection.add(items[x]); + } + } + return _collection; + } +} + diff --git a/example/web/form_controller.html b/example/web/form_controller.html new file mode 100644 index 000000000..ce5eb0fbd --- /dev/null +++ b/example/web/form_controller.html @@ -0,0 +1,166 @@ +
+
+ +
+

Wallpaper Resolution Size

+
+
+ +
+
+
+ +
+

Account Details

+
+ + +
+ +
+ + +
+ +
+ +
+ +
You did not enter your age properly
+ +
+ +
+ +
You did not enter any additional info
+
+ +
+

Colors

+ + New Color + +
+
+
+ +
+
+
+
+ + + +
+
+ + + +
+
+
+ +
+
+
+ +
+
+
+ + + +
+ + +
+
+
+
+
+
+ + + +
+

Your data has been successfully submitted...

+
+ +
+

There was an error

+ +

Account Details & Wallpaper

+
    +
  1. + You did not select a wallpaper resolution size. +
  2. + +
  3. + You did not enter your first name. +
  4. + +
  5. + You did not enter your last name. +
  6. + +
  7. + You left your email address blank. +
  8. +
  9. + You did not enter your email addresscorrectly +
  10. + +
  11. + You did not select your age +
  12. +
  13. + You have selected an invalid age +
  14. + +
  15. + You did not enter what city you live in +
  16. + +
  17. + You did not enter any additional info about yourself +
  18. + +
  19. + You did not input enough text for your additional info +
  20. +
+ +
+
+

Colors

+

One or more of the colors within the form is invalid...

+
    +
  1. +
    + You did not set a valid color for one of the color inputs. +
    +
    + You left a color input empty. +
    +
  2. +
+
+
+
+
diff --git a/example/web/hello_world.dart b/example/web/hello_world.dart index 871164ace..532235796 100644 --- a/example/web/hello_world.dart +++ b/example/web/hello_world.dart @@ -1,9 +1,7 @@ import 'package:angular/angular.dart'; import 'package:angular/application_factory.dart'; -@Controller( - selector: '[hello-world-controller]', - publishAs: 'ctrl') +@Injectable() class HelloWorld { String name = "world"; String color = "#aaaaaa"; @@ -11,6 +9,6 @@ class HelloWorld { main() { applicationFactory() - .addModule(new Module()..bind(HelloWorld)) + .rootContextType(HelloWorld) .run(); } diff --git a/example/web/hello_world.html b/example/web/hello_world.html index addcb05b2..e5fd620ed 100644 --- a/example/web/hello_world.html +++ b/example/web/hello_world.html @@ -5,9 +5,9 @@ -

Hello {{ctrl.name}}!

-Name: -Color: +

Hello {{name}}!

+Name: +Color: diff --git a/example/web/todo.dart b/example/web/todo.dart index b4c1c7bbb..4135dfa51 100644 --- a/example/web/todo.dart +++ b/example/web/todo.dart @@ -6,6 +6,8 @@ import 'package:angular/angular.dart'; import 'package:angular/application_factory.dart'; import 'package:angular/playback/playback_http.dart'; +import 'package:quiver/collection.dart'; + class Item { String text; bool done; @@ -53,10 +55,7 @@ class HttpServer implements Server { } } - -@Controller( - selector: '[todo-controller]', - publishAs: 'todo') +@Injectable() class Todo { var items = []; Item newItem; @@ -92,20 +91,25 @@ class Todo { int remaining() => items.fold(0, (count, item) => count += item.done ? 0 : 1); } + +// Temporary workaround, because context needs to extend Map. +@Injectable() +class TodoHashMap extends DelegatingMap { + final Map _delegate; + TodoHashMap(Todo todo) : _delegate = new Map() { + _delegate['todo'] = todo; + } + Map get delegate => _delegate; +} + main() { print(window.location.search); - var module = new Module() - ..bind(Todo) - ..bind(PlaybackHttpBackendConfig); + var module = new Module()..bind(PlaybackHttpBackendConfig)..bind(Todo); // If these is a query in the URL, use the server-backed // TodoController. Otherwise, use the stored-data controller. var query = window.location.search; - if (query.contains('?')) { - module.bind(Server, toImplementation: HttpServer); - } else { - module.bind(Server, toImplementation: NoOpServer); - } + module.bind(Server, toImplementation: query.contains('?') ? HttpServer : NoOpServer); if (query == '?record') { print('Using recording HttpBackend'); @@ -119,5 +123,8 @@ main() { module.bind(HttpBackend, toImplementation: PlaybackHttpBackend); } - applicationFactory().addModule(module).run(); + applicationFactory() + .rootContextType(TodoHashMap) + .addModule(module) + .run(); } diff --git a/example/web/todo.html b/example/web/todo.html index cbd32cc92..abb0f7474 100644 --- a/example/web/todo.html +++ b/example/web/todo.html @@ -11,7 +11,7 @@
Wait, Dart is loading this awesome app...
-
+

Things To Do ;-)

diff --git a/lib/application.dart b/lib/application.dart index 8c7414305..d25f34a14 100644 --- a/lib/application.dart +++ b/lib/application.dart @@ -8,15 +8,9 @@ * import 'package:angular/angular.dart'; * import 'package:angular/application_factory.dart'; * - * class MyModule extends Module { - * MyModule() { - * bind(HelloWorldController); - * } - * } - * * main() { * applicationFactory() - * .addModule(new MyModule()) + * .rootContextType(HelloWorldController) * .run(); * } * @@ -151,7 +145,6 @@ abstract class Application { dom.Element selector(String selector) => element = _find(selector); Application(): element = _find('[ng-app]', dom.window.document.documentElement) { - traceDetectWTF(context); modules.add(ngModule); ngModule..bind(VmTurnZone, toValue: zone) ..bind(Application, toValue: this) diff --git a/lib/application_factory.dart b/lib/application_factory.dart index f8b543982..3d1100e9b 100644 --- a/lib/application_factory.dart +++ b/lib/application_factory.dart @@ -50,7 +50,6 @@ import 'dart:html'; metaTargets: const [ Injectable, Decorator, - Controller, Component, Formatter ]) diff --git a/lib/application_factory_static.dart b/lib/application_factory_static.dart index f0c7d59b2..6dcbe83d0 100644 --- a/lib/application_factory_static.dart +++ b/lib/application_factory_static.dart @@ -11,15 +11,9 @@ * import 'package:angular/angular.dart'; * import 'package:angular/application_factory_static.dart'; * - * class MyModule extends Module { - * MyModule() { - * bind(HelloWorldController); - * } - * } - * * main() { * staticApplicationFactory() - * .addModule(new MyModule()) + * .rootContextType(HelloWorldController) * .run(); * } * @@ -32,9 +26,9 @@ library angular.app.factory.static; import 'package:angular/application.dart'; import 'package:angular/core/registry.dart'; +import 'package:angular/core/parser/dynamic_parser.dart'; import 'package:angular/core/parser/parser.dart'; import 'package:angular/core/parser/static_closure_map.dart'; -import 'package:angular/core/parser/dynamic_parser.dart'; import 'package:angular/core/registry_static.dart'; import 'package:angular/change_detection/change_detection.dart'; import 'package:angular/change_detection/dirty_checking_change_detector_static.dart'; @@ -67,7 +61,7 @@ class _StaticApplication extends Application { * * main() { * applicationFactory() - * .addModule(new Module()..bind(HelloWorld)) + * .rootContextType(HelloWorld) * .run(); * } * @@ -79,7 +73,7 @@ class _StaticApplication extends Application { * generated_static_expressions.getters, * generated_static_expressions.setters, * generated_static_expressions.symbols) - * .addModule(new Module()..bind(HelloWorldController)) + * .rootContextType(HelloWorld) * .run(); * */ diff --git a/lib/core/annotation.dart b/lib/core/annotation.dart index 38f43ac6c..b3361d8e3 100644 --- a/lib/core/annotation.dart +++ b/lib/core/annotation.dart @@ -8,6 +8,7 @@ import "dart:html" show ShadowRoot; export "package:angular/core/annotation_src.dart" show AttachAware, DetachAware, + ShadowRootAware, Formatter, DirectiveBinder, @@ -15,7 +16,6 @@ export "package:angular/core/annotation_src.dart" show Directive, Component, - Controller, Decorator, Visibility, diff --git a/lib/core/annotation_src.dart b/lib/core/annotation_src.dart index b2384b5b1..bbddf568a 100644 --- a/lib/core/annotation_src.dart +++ b/lib/core/annotation_src.dart @@ -11,7 +11,7 @@ abstract class DirectiveBinder { Visibility visibility: Visibility.LOCAL}); } -typedef void DirectiveBinderFn(DirectiveBinder module); +typedef void DirectiveBinderFn(DirectiveBinder binder); RegExp _ATTR_NAME = new RegExp(r'\[([^\]]+)\]$'); @@ -26,27 +26,27 @@ class Visibility { final String name; const Visibility._(this.name); - toString() => 'Visibility: $name'; + String toString() => 'Visibility: $name'; } /** - * Abstract supper class of [Controller], [Component], and [Decorator]. + * Abstract supper class of [Component], and [Decorator]. */ abstract class Directive { /// The directive can only be injected to other directives on the same element. - @deprecated // ('Use Visibility.LOCAL instead') + @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. - @deprecated// ('Use Visibility.CHILDREN instead') + @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. */ - @deprecated// ('Use Visibility.DIRECT_CHILD instead') + @Deprecated('Use Visibility.DIRECT_CHILD instead') static const Visibility DIRECT_CHILDREN_VISIBILITY = Visibility.DIRECT_CHILD; /** @@ -90,26 +90,22 @@ abstract class Directive { static const String IGNORE_CHILDREN = 'ignore'; /** - * A directive/component controller class can be injected into other - * directives/components. This attribute controls whether the - * controller is available to others. + * A directive/component controller class can be injected into other directives/components. This + * attribute controls whether the controller is available to others. * - * * `local` [Directive.LOCAL_VISIBILITY] - the controller can be injected - * into other directives / components on the same DOM element. - * * `children` [Directive.CHILDREN_VISIBILITY] - the controller can be - * injected into other directives / components on the same or child DOM - * elements. - * * `direct_children` [Directive.DIRECT_CHILDREN_VISIBILITY] - the - * controller can be injected into other directives / components on the - * direct children of the current DOM element. + * * [Visibility.LOCAL] - the controller can be injected into other directives / components on the + * same DOM element. + * * [Visibility.CHILDREN] - the controller can be injected into other directives / components on + * the same or child DOM elements. + * * [Visibility.DIRECT_CHILD] - the controller can be injected into other directives / components + * on the direct children of the current DOM element. */ final Visibility visibility; /** - * A directive/component class can publish types by using a factory - * function to generate a module. The module is then installed into - * the injector at that element. Any types declared in the module then - * become available for injection. + * A directive/component class can publish types by using a factory function to generate a module. + * The module is then installed into the injector at that element. Any types declared in the + * module then become available for injection. * * Example: * @@ -121,11 +117,10 @@ abstract class Directive { * binder.bind(SomeTypeA, visibility: Directive.LOCAL_VISIBILITY); * } * - * When specifying types, factories or values in the module, notice that - * `Visibility` maps to: - * * [Directive.LOCAL_VISIBILITY] - * * [Directive.CHILDREN_VISIBILITY] - * * [Directive.DIRECT_CHILDREN_VISIBILITY] + * `visibility` is one of: + * * [Visibility.LOCAL] + * * [Visibility.CHILDREN] (default) + * * [Visibility.DIRECT_CHILD] */ final DirectiveBinderFn module; @@ -152,12 +147,10 @@ abstract class Directive { * watch on expression. Once the expression turns truthy it will no longer * update. (cost: 1 watches until not null, then 0 watches) * - * * `&` - Treat the DOM attribute value as an expression. The expression is - * compiled and bound to the scope context. The resulting [BoundExpression] - * is assigned to the designated field. The component can evaluate the - * expression by calling the [BoundExpression] when needed. This is useful - * for passing expressions into controllers which act like callbacks. (cost: - * 0 watches) + * * `&` - Treat the DOM attribute value as an expression. Assign a closure + * function into the field. This allows the component to control + * the invocation of the closure. This is useful for passing + * expressions into controllers which act like callbacks. (cost: 0 watches) * * Example: * @@ -174,25 +167,25 @@ abstract class Directive { * class MyComponent { * String title; * var currentItem; - * BoundExpression onChange; + * ParsedFn onChange; * } * - * The above example shows how all three mapping modes are used: + * The above example shows how all three mapping modes are used. * - * * `@title` maps the title DOM attribute to the component `title` + * * `@title` maps the title DOM attribute to the controller `title` * field. Notice that this maps the content of the attribute, which * means that it can be used with `{{}}` interpolation. * * * `<=>currentItem` maps the expression (in this case the `selectedItem` - * in the current scope into the `currentItem` in the component. Notice + * in the current scope into the `currentItem` in the controller. Notice * that mapping is bi-directional. A change either in field or on * parent scope will result in change to the other. * - * * `&onChange` parse the expression (`doSomething()`), bind it to the - * parent context, the resulting [BoundExpression] is assigned to the - * controller `onChange` field. The [BoundExpression] is a callable object - * which can be invoked at any time by the component. The invocation of - * `onChange` will result in the expression `doSomething()` to be executed. + * * `&onChange` maps the expression into the controller `onChange` + * field. The result of mapping is a callable function which can be + * invoked at any time by the controller. The invocation of the + * callable function will result in the expression `doSomething()` to + * be executed in the parent context. */ final Map map; @@ -220,12 +213,10 @@ abstract class Directive { this.exportExpressionAttrs: const [] }); - String toString() => selector; - + toString() => selector; Directive _cloneWithNewMap(newMap); } - bool _applyAuthorStylesDeprecationWarningPrinted = false; bool _resetStyleInheritanceDeprecationWarningPrinted = false; @@ -266,10 +257,8 @@ class Component extends Directive { /** * Set the shadow root applyAuthorStyles property. See shadow-DOM * documentation for further details. - * - * This feature will be removed in Chrome 35. */ - @deprecated + @Deprecated('in Chrome 35') bool get applyAuthorStyles { if (!_applyAuthorStylesDeprecationWarningPrinted && _applyAuthorStyles == true) { print("WARNING applyAuthorStyles is deprecated in component $selector"); @@ -282,10 +271,8 @@ class Component extends Directive { /** * Set the shadow root resetStyleInheritance property. See shadow-DOM * documentation for further details. - * - * This feature will be removed in Chrome 35. */ - @deprecated + @Deprecated('in Chrome 35') bool get resetStyleInheritance { if (!_resetStyleInheritanceDeprecationWarningPrinted && _resetStyleInheritance == true) { print("WARNING resetStyleInheritance is deprecated in component $selector"); @@ -402,62 +389,6 @@ class Decorator extends Directive { exportExpressionAttrs: exportExpressionAttrs); } -/** - * Annotation placed on a class which should act as a controller for your - * application. - * - * Controllers are essentially [Decorator]s with few key differences: - * - * * Controllers create a new scope at the element. - * * Controllers should not do any DOM manipulation. - * * Controllers are meant for application-logic - * (rather then DOM manipulation logic which directives are meant for.) - * - * Controllers can implement [AttachAware], [DetachAware] and - * declare these optional methods: - * - * * `attach()` - Called on first [Scope.apply()]. - * * `detach()` - Called on when owning scope is destroyed. - */ -@deprecated -class Controller extends Decorator { - /** - * An expression under which the controller instance will be published into. - * This allows the expressions in the template to be referring to controller - * instance and its properties. - */ - final String publishAs; - - const Controller({ - children: Directive.COMPILE_CHILDREN, - this.publishAs, - map, - DirectiveBinderFn module, - selector, - visibility, - exportExpressions, - exportExpressionAttrs - }) - : super(selector: selector, - children: children, - visibility: visibility, - map: map, - module: module, - exportExpressions: exportExpressions, - exportExpressionAttrs: exportExpressionAttrs); - - Directive _cloneWithNewMap(newMap) => - new Controller( - children: children, - publishAs: publishAs, - module: module, - map: newMap, - selector: selector, - visibility: visibility, - exportExpressions: exportExpressions, - exportExpressionAttrs: exportExpressionAttrs); -} - /** * Abstract supper class of [NgAttr], [NgCallback], [NgOneWay], [NgOneWayOneTime], and [NgTwoWay]. */ @@ -514,9 +445,10 @@ class NgTwoWay extends DirectiveAnnotation { } /** - * When applied as an annotation on a directive field specifies that the field is to be mapped to - * DOM attribute with the provided [attrName]. The value of the attribute to be treated as a - * an expression, equivalent to `&` specification. + * When applied as an annotation on a directive field specifies that + * the field is to be mapped to DOM attribute with the provided [attrName]. + * The value of the attribute to be treated as a callback expression, + * equivalent to `&` specification. */ class NgCallback extends DirectiveAnnotation { final _mappingSpec = '&'; @@ -548,8 +480,8 @@ abstract class DetachAware { * For more on formatters in Angular, see the documentation for the * [angular:formatter](#angular-formatter) library. * - * A formatter class must have a call method with at least one parameter, which specifies the value to format. Any - * additional parameters are treated as arguments of the formatter. + * A formatter class must have a call method with at least one parameter, which specifies the value + * to format. Any additional parameters are treated as arguments of the formatter. * * **Usage** * @@ -575,5 +507,5 @@ class Formatter { const Formatter({this.name}); - toString() => 'Formatter: $name'; + String toString() => 'Formatter: $name'; } diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart index 5049a04ac..fbec8a1b4 100644 --- a/lib/core_dom/element_binder.dart +++ b/lib/core_dom/element_binder.dart @@ -202,9 +202,6 @@ class ElementBinder { var directive; try { directive = directiveInjector.getByKey(ref.typeKey); - if (ref.annotation is Controller) { - scope.parentScope.context[(ref.annotation as Controller).publishAs] = directive; - } var tasks = directive is AttachAware ? new _TaskList(() { if (scope.isAttached) directive.attach(); @@ -276,9 +273,6 @@ class ElementBinder { for(var i = 0; i < directiveRefs.length; i++) { DirectiveRef ref = directiveRefs[i]; Directive annotation = ref.annotation; - if (ref.annotation is Controller) { - scope = nodeInjector.scope = scope.createChild(new PrototypeMap(scope.context)); - } _createDirectiveFactories(ref, nodeInjector, node, nodeAttrs); if (ref.annotation.module != null) { DirectiveBinderFn config = ref.annotation.module; diff --git a/lib/core_dom/shadow_dom_component_factory.dart b/lib/core_dom/shadow_dom_component_factory.dart index f34e27bd0..7d1a8a8eb 100644 --- a/lib/core_dom/shadow_dom_component_factory.dart +++ b/lib/core_dom/shadow_dom_component_factory.dart @@ -22,12 +22,12 @@ abstract class BoundComponentFactory { return null; } - static void _setupOnShadowDomAttach(controller, TemplateLoader templateLoader, + static void _setupOnShadowDomAttach(component, TemplateLoader templateLoader, Scope shadowScope) { - if (controller is ShadowRootAware) { + if (component is ShadowRootAware) { templateLoader.template.then((shadowDom) { if (!shadowScope.isAttached) return; - (controller as ShadowRootAware).onShadowRoot(shadowDom); + (component as ShadowRootAware).onShadowRoot(shadowDom); }); } } diff --git a/lib/transformer.dart b/lib/transformer.dart index f8637a8ea..46eaaa3a3 100644 --- a/lib/transformer.dart +++ b/lib/transformer.dart @@ -35,7 +35,6 @@ TransformOptions _parseSettings(Map args) { var annotations = [ 'di.annotations.Injectable', 'angular.core.annotation_src.Decorator', - 'angular.core.annotation_src.Controller', 'angular.core.annotation_src.Component', 'angular.core.annotation_src.Formatter']; annotations.addAll(_readStringListValue(args, 'injectable_annotations')); diff --git a/scripts/travis/build.sh b/scripts/travis/build.sh index f6dc8e263..c6064c29e 100755 --- a/scripts/travis/build.sh +++ b/scripts/travis/build.sh @@ -55,10 +55,10 @@ if [[ $TESTS == "dart2js" ]]; then echo '-- BUILDING: verify dart2js size --' echo '-----------------------------------' cd $NGDART_BASE_DIR/example - checkSize build/web/animation.dart.js 208021 - checkSize build/web/bouncing_balls.dart.js 202325 - checkSize build/web/hello_world.dart.js 210000 - checkSize build/web/todo.dart.js 203121 + checkSize build/web/animation.dart.js 224697 + checkSize build/web/bouncing_balls.dart.js 223927 + checkSize build/web/hello_world.dart.js 221838 + checkSize build/web/todo.dart.js 224414 if ((SIZE_TOO_BIG_COUNT > 0)); then exit 1 else diff --git a/test/angular_spec.dart b/test/angular_spec.dart index bc30ab523..463a0f17f 100644 --- a/test/angular_spec.dart +++ b/test/angular_spec.dart @@ -99,7 +99,6 @@ main() { "angular.core.annotation.ShadowRootAware", "angular.core.annotation_src.AttachAware", "angular.core.annotation_src.Component", - "angular.core.annotation_src.Controller", "angular.core.annotation_src.Decorator", "angular.core.annotation_src.DetachAware", "angular.core.annotation_src.Directive", diff --git a/test/core/annotation_src_spec.dart b/test/core/annotation_src_spec.dart index dec1b2c9b..2c47fb49e 100644 --- a/test/core/annotation_src_spec.dart +++ b/test/core/annotation_src_spec.dart @@ -76,25 +76,4 @@ void main() => describe('annotations', () { expect(variables(cloneWithNewMap(decorator, {}))).toEqual(variables(decorator)); }); }); - - describe('controller', () { - it('should set all fields on clone when all the fields are set', () { - var controller = new Controller( - publishAs: '', - children: 'xxx', - map: {}, - selector: '', - module: (i){}, - visibility: Directive.LOCAL_VISIBILITY, - exportExpressions: [], - exportExpressionAttrs: [] - ); - - // Check that no fields are null - expect(nullFields(controller)).toEqual([]); - - // Check that the clone is the same as the original. - expect(variables(cloneWithNewMap(controller, {}))).toEqual(variables(controller)); - }); - }); }); diff --git a/test/core_dom/compiler_spec.dart b/test/core_dom/compiler_spec.dart index 5ad29d9ee..8f92103d3 100644 --- a/test/core_dom/compiler_spec.dart +++ b/test/core_dom/compiler_spec.dart @@ -50,10 +50,6 @@ void main() { ..bind(LocalAttrDirective) ..bind(OneOfTwoDirectives) ..bind(TwoOfTwoDirectives) - ..bind(MyController) - ..bind(MyParentController) - ..bind(MyChildController) - ..bind(MyScopeModifyingController) ..bind(SameNameDecorator) ..bind(SameNameTransclude) ..bind(ScopeAwareComponent) @@ -291,7 +287,6 @@ void main() { ..bind(NonAssignableMappingComponent) ..bind(ParentExpressionComponent) ..bind(PublishMeComponent) - ..bind(PublishMeDirective) ..bind(LogComponent) ..bind(AttachDetachComponent) ..bind(SimpleAttachComponent) @@ -732,21 +727,6 @@ void main() { } })); - it('should publish component controller into the scope', async(() { - var element = _.compile(r'
'); - microLeap(); - _.rootScope.apply(); - expect(element).toHaveText('WORKED'); - })); - - it('should publish directive controller into the scope', async((VmTurnZone zone) { - var element = _.compile(r'
{{ctrlName.value}}
'); - - microLeap(); - _.rootScope.apply(); - expect(element.text).toEqual('WORKED'); - })); - it('should "publish" controller to injector under provided module', () { _.compile(r'
'); expect(PublishModuleAttrDirective._injector.get(PublishModuleAttrDirective)). @@ -1054,39 +1034,6 @@ void main() { expect(log.result()).toEqual('IncludeTransclude; SimpleTransclude'); })); - it('should expose a parent controller to the scope of its children', (TestBed _) { - var element = _.compile('
' - '
{{ my_parent.data() }}
' - '
'); - - _.rootScope.apply(); - - expect(element.text).toContain('my data'); - }); - - it('should pass the right scope into inner mustache', (TestBed _) { - var element = _.compile('
' - '
{{ data }}
' - '
'); - - _.rootScope.apply(); - - expect(element.text).toContain('my data'); - }); - - it('should expose a ancestor controller to the scope of its children thru a undecorated element', (TestBed _) { - var element = _.compile( - '
' - '
' - '
{{ my_parent.data() }}
' - '
' - '
'); - - _.rootScope.apply(); - - expect(element.text).toContain('my data'); - }); - it('should call scope setter on ScopeAware components', async((TestBed _, Logger log) { var element = _.compile(''); @@ -1098,15 +1045,6 @@ void main() { describe('Decorator', () { - it('should allow creation of a new scope', () { - _.rootScope.context['name'] = 'cover me'; - _.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(); @@ -1137,28 +1075,6 @@ void main() { })); } -@Controller( - selector: '[my-scope-modifying-controller]') -class MyScopeModifyingController { - MyScopeModifyingController(Scope s) { - s.context['data'] = 'my data'; - } -} - -@Controller( - selector: '[my-parent-controller]', - publishAs: 'my_parent') -class MyParentController { - data() { - return "my data"; - } -} - -@Controller( - selector: '[my-child-controller]', - publishAs: 'my_child') -class MyChildController {} - @Component( selector: 'tab', visibility: Directive.DIRECT_CHILDREN_VISIBILITY) @@ -1451,14 +1367,6 @@ class PublishMeComponent { String value = 'WORKED'; } -@Controller ( - selector: '[publish-me]', - publishAs: 'ctrlName') -class PublishMeDirective { - String value = 'WORKED'; -} - - @Component( selector: 'log', template: r'', @@ -1501,15 +1409,6 @@ class AttachDetachComponent implements AttachAware, DetachAware, ShadowRootAware } } -@Controller( - selector: '[my-controller]', - publishAs: 'myCtrl') -class MyController { - MyController(Scope scope) { - scope.context['name'] = 'MyController'; - } -} - @Component() class MissingSelector {} diff --git a/test/core_dom/event_handler_spec.dart b/test/core_dom/event_handler_spec.dart index 68a6784a6..036e19986 100644 --- a/test/core_dom/event_handler_spec.dart +++ b/test/core_dom/event_handler_spec.dart @@ -2,12 +2,6 @@ library event_handler_spec; import '../_specs.dart'; -@Controller(selector: '[foo]', publishAs: 'ctrl') -class FooController { - var description = "desc"; - var invoked = false; -} - @Component(selector: 'bar', template: '''
@@ -28,7 +22,6 @@ main() { Element ngAppElement; beforeEachModule((Module module) { ngAppElement = new DivElement()..attributes['ng-app'] = ''; - module..bind(FooController); module..bind(BarComponent); module..bind(Node, toValue: ngAppElement); document.body.append(ngAppElement); @@ -47,34 +40,26 @@ main() { it('should register and handle event', (TestBed _) { var e = compile(_, - '''
-
-
'''); + '''
'''); - _.triggerEvent(e.querySelector('[on-abc]'), 'abc'); - expect(_.getScope(e).context['ctrl'].invoked).toEqual(true); + _.triggerEvent(e, 'abc'); + expect(_.rootScope.context['invoked']).toEqual(true); }); it('shoud register and handle event with long name', (TestBed _) { var e = compile(_, - '''
-
-
'''); + '''
'''); - _.triggerEvent(e.querySelector('[on-my-new-event]'), 'myNewEvent'); - var fooScope = _.getScope(e); - expect(fooScope.context['ctrl'].invoked).toEqual(true); + _.triggerEvent(e, 'myNewEvent'); + expect(_.rootScope.context['invoked']).toEqual(true); }); it('shoud have model updates applied correctly', (TestBed _) { var e = compile(_, - '''
-
{{ctrl.description}}
-
'''); - var el = document.querySelector('[on-abc]'); - el.dispatchEvent(new Event('abc')); + '''
{{description}}
'''); + e.dispatchEvent(new Event('abc')); _.rootScope.apply(); - expect(el.text).toEqual("new description"); + expect(e.text).toEqual("new description"); }); it('shoud register event when shadow dom is used', async((TestBed _) { @@ -91,11 +76,10 @@ main() { it('shoud handle event within content only once', async((TestBed _) { var e = compile(_, - '''
- + '''
-
'''); + '''); microLeap(); @@ -105,8 +89,7 @@ main() { BarComponent ctrl = shadowRootScope.context['ctrl']; expect(ctrl.invoked).toEqual(false); - var fooScope = _.getScope(document.querySelector('[foo]')); - expect(fooScope.context['ctrl'].invoked).toEqual(true); + expect(_.rootScope.context['ctrl']['invoked']).toEqual(true); })); }); } diff --git a/test/directive/ng_model_spec.dart b/test/directive/ng_model_spec.dart index 2f5f98e51..1c2a3ae30 100644 --- a/test/directive/ng_model_spec.dart +++ b/test/directive/ng_model_spec.dart @@ -40,14 +40,14 @@ void main() { beforeEachModule((Module module) { module - ..bind(ControllerWithNoLove) + ..bind(ComponentWithNoLove) ..bind(MyCustomInputValidator) ..bind(CountingValidator); }); beforeEach((TestBed tb) { _ = tb; - dirInjector = new DirectiveInjector(null, _.injector, null, null, null, null, null, null); + dirInjector = new DirectiveInjector(null, _.injector, null, null, null, null, null); }); describe('type="text" like', () { @@ -1425,16 +1425,13 @@ void main() { describe('error messages', () { it('should produce a useful error for bad ng-model expressions', () { + // On Dartium, this fails with "...no instance getter..." + // On dart2js, "...method not found..." expect(async(() { - _.compile('
'); + microLeap(); _.rootScope.apply(); - })).toThrow('love'); - + })).toThrow(); }); }); @@ -1669,11 +1666,11 @@ void main() { }); } -@Controller( - selector: '[no-love]', +@Component( + selector: 'no-love', + template: '', publishAs: 'ctrl') -class ControllerWithNoLove { - var apathy = null; +class ComponentWithNoLove { } class LowercaseValueParser implements NgModelConverter { diff --git a/test_e2e/hello_world_spec.dart b/test_e2e/hello_world_spec.dart index d27f51191..0556790d9 100644 --- a/test_e2e/hello_world_spec.dart +++ b/test_e2e/hello_world_spec.dart @@ -8,8 +8,8 @@ main() { beforeEach(() { protractor.getInstance().get('hello_world.html'); - nameByModel = element(by.model('ctrl.name')); - nameByBinding = element(by.binding('ctrl.name')); + nameByModel = element(by.model('name')); + nameByBinding = element(by.binding('name')); }); it('should set initial value for input element', () { @@ -17,7 +17,7 @@ main() { }); it('should set mustache value to initial value of model', () { - nameByBinding = element(by.binding('ctrl.name')); + nameByBinding = element(by.binding('name')); expect(nameByBinding.getText()).toEqual('Hello world!'); });