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!'); });