diff --git a/benchmark/lexer_perf.dart b/benchmark/lexer_perf.dart
index 80aff438d..65533f48d 100644
--- a/benchmark/lexer_perf.dart
+++ b/benchmark/lexer_perf.dart
@@ -5,16 +5,10 @@ import 'package:angular/core/parser/lexer.dart';
main() {
Lexer lexer = new Lexer();
- time('ident', () =>
- lexer.call('ctrl foo baz ctrl.foo ctrl.bar ctrl.baz'));
- time('ident-path', () =>
- lexer.call('a.b a.b.c a.b.c.d a.b.c.d.e.f'));
- time('num', () =>
- lexer.call('1 23 34 456 12341234 12351235'));
- time('num-double', () =>
- lexer.call('.0 .1 .12 0.123 0.1234'));
- time('string', () =>
- lexer.call("'quick brown dog and fox say what'"));
- time('string-escapes', () =>
- lexer.call("quick '\\' brown \u1234 dog and fox\n\rsay what'"));
+ time('ident', () => lexer.call('ctrl foo baz ctrl.foo ctrl.bar ctrl.baz'));
+ time('ident-path', () => lexer.call('a.b a.b.c a.b.c.d a.b.c.d.e.f'));
+ time('num', () => lexer.call('1 23 34 456 12341234 12351235'));
+ time('num-double', () => lexer.call('.0 .1 .12 0.123 0.1234'));
+ time('string', () => lexer.call("'quick brown dog and fox say what'"));
+ time('string-escapes', () => lexer.call("quick '\\' brown \u1234 dog and fox\n\rsay what'"));
}
diff --git a/example/web/animation.dart b/example/web/animation.dart
index a60f569d2..164ec84bd 100644
--- a/example/web/animation.dart
+++ b/example/web/animation.dart
@@ -9,9 +9,6 @@ part 'animation/visibility_demo.dart';
part 'animation/stress_demo.dart';
part 'animation/css_demo.dart';
-@Controller(
- selector: '[animation-demo]',
- publishAs: 'demo')
class AnimationDemo {
final pages = ["About", "ng-repeat", "Visibility", "Css", "Stress Test"];
var currentPage = "About";
@@ -24,11 +21,11 @@ class AnimationDemoModule extends Module {
bind(VisibilityDemo);
bind(StressDemo);
bind(CssDemo);
- bind(AnimationDemo);
}
}
main() {
applicationFactory()
.addModule(new AnimationDemoModule())
+ .rootContextType(AnimationDemo)
.run();
}
diff --git a/example/web/animation.html b/example/web/animation.html
index d0728b00b..5373d98c4 100644
--- a/example/web/animation.html
+++ b/example/web/animation.html
@@ -6,14 +6,15 @@
-
+
+
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/animation/css_demo.dart b/example/web/animation/css_demo.dart
index a09018595..5312556f3 100644
--- a/example/web/animation/css_demo.dart
+++ b/example/web/animation/css_demo.dart
@@ -4,25 +4,24 @@ part of animation;
selector: 'css-demo',
template: '''
-
+
Toggle A
-
+
Toggle B
-
+
Toggle C
BOX
+ 'a': stateA,
+ 'b': stateB,
+ 'c': stateC}">BOX
''',
- publishAs: 'ctrl',
applyAuthorStyles: true)
class CssDemo {
bool stateA = false;
diff --git a/example/web/animation/repeat_demo.dart b/example/web/animation/repeat_demo.dart
index 5857d72fe..8352fee81 100644
--- a/example/web/animation/repeat_demo.dart
+++ b/example/web/animation/repeat_demo.dart
@@ -4,19 +4,17 @@ part of animation;
selector: 'repeat-demo',
template: '''
+name:
diff --git a/example/web/todo.dart b/example/web/todo.dart
index b4c1c7bbb..27460be5c 100644
--- a/example/web/todo.dart
+++ b/example/web/todo.dart
@@ -53,10 +53,6 @@ class HttpServer implements Server {
}
}
-
-@Controller(
- selector: '[todo-controller]',
- publishAs: 'todo')
class Todo {
var items = [];
Item newItem;
@@ -94,18 +90,12 @@ class Todo {
main() {
print(window.location.search);
- var module = new Module()
- ..bind(Todo)
- ..bind(PlaybackHttpBackendConfig);
+ var module = new Module()..bind(PlaybackHttpBackendConfig);
// 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 +109,8 @@ main() {
module.bind(HttpBackend, toImplementation: PlaybackHttpBackend);
}
- applicationFactory().addModule(module).run();
+ applicationFactory()
+ .addModule(module)
+ .rootContextType(Todo)
+ .run();
}
diff --git a/example/web/todo.html b/example/web/todo.html
index cbd32cc92..2dcb69f4a 100644
--- a/example/web/todo.html
+++ b/example/web/todo.html
@@ -11,19 +11,19 @@
Wait, Dart is loading this awesome app...
-
+
Things To Do ;-)
- mark all done
- archive done
+ mark all done
+ archive done
-
Remaining {{todo.remaining()}} of {{todo.items.length}} items.
+
Remaining {{ remaining() }} of {{ items.length }} items.
-
+
@@ -31,9 +31,9 @@
Things To Do ;-)
diff --git a/lib/animate/module.dart b/lib/animate/module.dart
index 44a700613..119fa5ba8 100644
--- a/lib/animate/module.dart
+++ b/lib/animate/module.dart
@@ -4,11 +4,11 @@
* The [angular.animate](#angular/angular-animate) library makes it easier to build animations
* that affect the lifecycle of DOM elements. A useful example of this is animating the
* removal of an element from the DOM. In order to do this ideally the
- * operation should immediatly execute and manipulate the data model,
+ * operation should immediately execute and manipulate the data model,
* and the framework should handle the actual remove of the DOM element once
- * the animation complets. This ensures that the logic and model of the
- * application is seperated so that the state of the model can be reasoned
- * about without having to wory about future modifications of the model.
+ * the animation completes. This ensures that the logic and model of the
+ * application is separated so that the state of the model can be reasoned
+ * about without having to worry about future modifications of the model.
* This library uses computed css styles to calculate the total duration
* of an animation and handles the addition, removal, and modification of DOM
* elements for block level directives such as `ng-if`, `ng-repeat`,
@@ -16,15 +16,14 @@
*
* To use, install the AnimationModule into your main module:
*
- * var module = new Module()
- * ..install(new AnimationModule());
+ * var module = new Module()..install(new AnimationModule());
*
* Once the module has been installed, all block level DOM manipulations will
* be routed through the [CssAnimate] class instead of the
* default [NgAnimate] implementation. This will, in turn,
* perform the tracking, manipulation, and computation for animations.
*
- * As an example of how this works, let's walk through what happens whan an
+ * As an example of how this works, let's walk through what happens when an
* element is added to the DOM. The [CssAnimate] implementation will add the
* `.ng-enter` class to new DOM elements when they are inserted into the DOM
* by a directive and will read the computed style. If there is a
@@ -35,7 +34,7 @@
* precomputed duration) the `.ng-enter` and `.ng-enter-active` classes
* will be removed from the DOM element.
*
- * When removing elements from the DOM, a simliar pattern is followed. The
+ * When removing elements from the DOM, a similar pattern is followed. The
* `.ng-leave` class will be added to an element, the transition and / or
* keyframe animation duration will be computed, and if it is non-zero the
* animation will be run by adding the `.ng-leave-active` class. When
@@ -58,7 +57,7 @@
* Fade out example:
*
* HTML:
- *
+ *
* Goodbye world!
*
*
@@ -71,8 +70,8 @@
* opacity: 0;
* }
*
- * This will perform a fade out animation on the 'goodby' div when the
- * `ctrl.visible` property goes from `true` to `false`.
+ * This will perform a fade out animation on the 'goodbye' div when the
+ * `visible` property goes from `true` to `false`.
*
* The [CssAnimate] will also do optimizations on running animations by
* preventing child DOM animations with the [AnimationOptimizer]. This
@@ -129,11 +128,11 @@ final Logger _logger = new Logger('ng.animate');
* of view construction, and some of the native directives to allow you to add
* and define css transition and keyframe animations for the styles of your
* elements.
- *
+ *
* Example html:
*
*
...
- *
+ *
* Example css defining an opacity transition over .5 seconds using the
* `.ng-enter` and `.ng-leave` css classes:
*
@@ -144,7 +143,7 @@ final Logger _logger = new Logger('ng.animate');
* .my-div.ng-enter-active {
* opacity: 1;
* }
- *
+ *
* .my-div.ng-leave {
* transition: all 500ms;
* opacity: 1;
diff --git a/lib/application.dart b/lib/application.dart
index b04f5f7e4..e9a30092e 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();
* }
*
@@ -90,7 +84,6 @@ import 'package:angular/introspection_js.dart';
* about Angular services, formatters, and directives. When writing tests, this is typically done for
* you by the [SetUpInjector](#angular-mock@id_setUpInjector) method.
*
-
*/
class AngularModule extends Module {
AngularModule() {
@@ -139,9 +132,7 @@ abstract class Application {
final List modules = [];
dom.Element element;
-/**
-* Creates a selector for a DOM element.
-*/
+ /// Creates a selector for a DOM element.
dom.Element selector(String selector) => element = _find(selector);
Application(): element = _find('[ng-app]', dom.window.document.documentElement) {
@@ -151,9 +142,7 @@ abstract class Application {
..bind(dom.Node, toFactory: (i) => i.get(Application).element);
}
-/**
-* Returns the injector for this module.
-*/
+ /// injector for this module.
Injector injector;
Application addModule(Module module) {
@@ -161,6 +150,11 @@ abstract class Application {
return this;
}
+ Application rootContextType(Type rootContext) {
+ modules.add(new Module()..bind(Object, toImplementation: rootContext));
+ return this;
+ }
+
Injector run() {
publishToJavaScript();
return zone.run(() {
diff --git a/lib/application_factory.dart b/lib/application_factory.dart
index 8c46d4d4d..26706a4a6 100644
--- a/lib/application_factory.dart
+++ b/lib/application_factory.dart
@@ -51,7 +51,6 @@ import 'dart:html';
metaTargets: const [
Injectable,
Decorator,
- Controller,
Component,
Formatter
])
@@ -62,7 +61,7 @@ class _DynamicApplication extends Application {
ngModule
..bind(MetadataExtractor, toImplementation: DynamicMetadataExtractor)
..bind(FieldGetterFactory, toImplementation: DynamicFieldGetterFactory)
- ..bind(ClosureMap, toImplementation: DynamicClosureMap);
+ ..bind(ClosureMap, toFactory: (_) => new ClosureMapLocalsAware(new DynamicClosureMap()));
}
Injector createInjector() => new DynamicInjector(modules: modules);
diff --git a/lib/application_factory_static.dart b/lib/application_factory_static.dart
index 2c4447722..2f0d0ebc0 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();
* }
*
@@ -57,7 +51,8 @@ class _StaticApplication extends Application {
ngModule
..bind(MetadataExtractor, toValue: new StaticMetadataExtractor(metadata))
..bind(FieldGetterFactory, toValue: new StaticFieldGetterFactory(fieldGetters))
- ..bind(ClosureMap, toValue: new StaticClosureMap(fieldGetters, fieldSetters, symbols));
+ ..bind(ClosureMap, toFactory: (_) => new ClosureMapLocalsAware(
+ new StaticClosureMap(fieldGetters, fieldSetters, symbols)));
}
Injector createInjector() =>
@@ -67,7 +62,7 @@ class _StaticApplication extends Application {
*
* Bootstraps Angular as part of the `main()` function.
*
-* `staticApplication()` replaces `dynamicApplication()` in the main function during pub build,
+* `staticApplication()` replaces `dynamicApplication()` in the main function during pub build,
* and is populated with the getters, setters, annotations, and factories generated by
* Angular's transformers for dart2js compilation. It is not typically called directly.
*
@@ -75,20 +70,20 @@ class _StaticApplication extends Application {
*
* main() {
* applicationFactory()
-* .addModule(new Module()..bind(HelloWorld))
-* .run();
+* .rootContextType(HelloWorld)
+* .run();
* }
*
* 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();
+* generated_static_metadata.typeAnnotations,
+* generated_static_expressions.getters,
+* generated_static_expressions.setters,
+* generated_static_expressions.symbols)
+* .rootContextType(HelloWorld)
+* .run();
*
*/
Application staticApplicationFactory(
diff --git a/lib/change_detection/dirty_checking_change_detector.dart b/lib/change_detection/dirty_checking_change_detector.dart
index ab940ac07..dd1d7fe81 100644
--- a/lib/change_detection/dirty_checking_change_detector.dart
+++ b/lib/change_detection/dirty_checking_change_detector.dart
@@ -2,6 +2,7 @@ library dirty_checking_change_detector;
import 'dart:collection';
import 'package:angular/change_detection/change_detection.dart';
+import 'package:angular/change_detection/watch_group.dart';
/**
* [DirtyCheckingChangeDetector] determines which object properties have changed
@@ -369,7 +370,7 @@ class _ChangeIterator implements Iterator>{
* removing efficient. [DirtyCheckingRecord] also has a [nextChange] field which
* creates a single linked list of all of the changes for efficient traversal.
*/
-class DirtyCheckingRecord implements Record, WatchRecord {
+class DirtyCheckingRecord implements WatchRecord {
static const List _MODE_NAMES =
const ['MARKER', 'IDENT', 'GETTER', 'MAP[]', 'ITERABLE', 'MAP'];
static const int _MODE_MARKER_ = 0;
@@ -414,9 +415,10 @@ class DirtyCheckingRecord implements Record, WatchRecord {
* [DirtyCheckingRecord] into different access modes. If Object it sets up
* reflection. If [Map] then it sets up map accessor.
*/
- void set object(obj) {
- _object = obj;
- if (obj == null) {
+ void set object(Object object) {
+ _object = object;
+
+ if (object == null) {
_mode = _MODE_IDENTITY_;
_getter = null;
return;
@@ -424,7 +426,8 @@ class DirtyCheckingRecord implements Record, WatchRecord {
if (field == null) {
_getter = null;
- if (obj is Map) {
+
+ if (object is Map) {
if (_mode != _MODE_MAP_) {
_mode = _MODE_MAP_;
currentValue = new _MapChangeRecord();
@@ -436,8 +439,10 @@ class DirtyCheckingRecord implements Record, WatchRecord {
// new reference.
currentValue._revertToPreviousState();
}
+ return;
+ }
- } else if (obj is Iterable) {
+ if (object is Iterable) {
if (_mode != _MODE_ITERABLE_) {
_mode = _MODE_ITERABLE_;
currentValue = new _CollectionChangeRecord();
@@ -449,6 +454,7 @@ class DirtyCheckingRecord implements Record, WatchRecord {
// new reference.
currentValue._revertToPreviousState();
}
+ return;
} else {
_mode = _MODE_IDENTITY_;
}
@@ -456,19 +462,32 @@ class DirtyCheckingRecord implements Record, WatchRecord {
return;
}
- if (obj is Map) {
+ if (object is Map) {
_mode = _MODE_MAP_FIELD_;
_getter = null;
- } else {
- if (_fieldGetterFactory.isMethod(obj, field)) {
- _mode = _MODE_IDENTITY_;
- previousValue = currentValue = _fieldGetterFactory.method(obj, field)(obj);
- assert(previousValue is Function);
- } else {
- _mode = _MODE_GETTER_;
- _getter = _fieldGetterFactory.getter(obj, field);
+ return;
+ }
+
+ if (object is ContextLocals) {
+ var ctx = object as ContextLocals;
+ if (ctx.hasProperty(field)) {
+ _object = object;
+ _mode = _MODE_MAP_FIELD_;
+ _getter = null;
+ return;
}
+ object = ctx.rootContext;
}
+
+ if (_fieldGetterFactory.isMethod(object, field)) {
+ _mode = _MODE_IDENTITY_;
+ previousValue = currentValue = _fieldGetterFactory.method(object, field)(object);
+ assert(previousValue is Function);
+ }
+
+ _mode = _MODE_GETTER_;
+ _getter = _fieldGetterFactory.getter(object, field);
+ currentValue = _getter(object);
}
bool check() {
@@ -520,8 +539,6 @@ class DirtyCheckingRecord implements Record, WatchRecord {
String toString() => '${_MODE_NAMES[_mode]}[$field]{$hashCode}';
}
-final Object _INITIAL_ = new Object();
-
class _MapChangeRecord implements MapChangeRecord {
final _records = new Map();
Map _map;
diff --git a/lib/change_detection/prototype_map.dart b/lib/change_detection/prototype_map.dart
index 130444184..779a09767 100644
--- a/lib/change_detection/prototype_map.dart
+++ b/lib/change_detection/prototype_map.dart
@@ -1,37 +1,47 @@
part of angular.watch_group;
-class PrototypeMap implements Map {
- final Map prototype;
- final Map self = new Map();
+// todo(vicb) rename the file
+class ContextLocals {
+ // todo(vicb) _parentContext
+ final Object parent;
+ Object _rootContext;
+ final Map _locals = {};
- PrototypeMap(this.prototype);
+ ContextLocals(this.parent, [Map locals = null]) {
+ assert(parent != null);
+ if (locals != null) _locals.addAll(locals);
+ _locals[r'$parent'] = parent;
+ }
+
+ static ContextLocals wrapper(context, Map locals) =>
+ new ContextLocals(context, locals);
- void operator []=(name, value) {
- self[name] = value;
+ dynamic get rootContext {
+ if (_rootContext == null) {
+ _rootContext = parent is ContextLocals ?
+ (parent as ContextLocals).rootContext :
+ parent;
+ }
+ return _rootContext;
}
- V operator [](name) => self.containsKey(name) ? self[name] : prototype[name];
-
- bool get isEmpty => self.isEmpty && prototype.isEmpty;
- bool get isNotEmpty => self.isNotEmpty || prototype.isNotEmpty;
- // todo(vbe) include prototype keys ?
- Iterable get keys => self.keys;
- // todo(vbe) include prototype values ?
- Iterable get values => self.values;
- int get length => self.length;
-
- void forEach(fn) {
- // todo(vbe) include prototype ?
- self.forEach(fn);
+
+ bool hasProperty(String prop) {
+ return _locals.containsKey(prop) ||
+ parent is ContextLocals && (parent as ContextLocals).hasProperty(prop);
}
- V remove(key) => self.remove(key);
- clear() => self.clear;
- // todo(vbe) include prototype ?
- bool containsKey(key) => self.containsKey(key);
- // todo(vbe) include prototype ?
- bool containsValue(key) => self.containsValue(key);
- void addAll(map) {
- self.addAll(map);
+
+ void operator[]=(String prop, value) {
+ _locals[prop] = value;
+ }
+
+ dynamic operator[](String prop) {
+ assert(hasProperty(prop));
+ var context = this;
+
+ while (!context._locals.containsKey(prop)) {
+ // todo(vicb) cache context where prop is defined
+ context = context.parent;
+ }
+ return context._locals[prop];
}
- // todo(vbe) include prototype ?
- V putIfAbsent(key, fn) => self.putIfAbsent(key, fn);
}
diff --git a/lib/change_detection/watch_group.dart b/lib/change_detection/watch_group.dart
index b0dbd05f0..1ad115548 100644
--- a/lib/change_detection/watch_group.dart
+++ b/lib/change_detection/watch_group.dart
@@ -15,8 +15,10 @@ part 'prototype_map.dart';
* * [value]: The current value of the watched expression.
* * [previousValue]: The previous value of the watched expression.
*
- * If the expression is watching a collection (a list or a map), then [value] is wrapped in
- * a [CollectionChangeItem] that lists all the changes.
+ * Notes:
+ *
+ * * [value] is a [CollectionChangeRecord] when a Collection is being watched
+ * * [value] is a [MapChangeRecord] when a [Map] is being watched
*/
typedef void ReactionFn(value, previousValue);
typedef void ChangeLog(String expression, current, previous);
@@ -771,33 +773,45 @@ class _EvalWatchRecord implements WatchRecord<_Handler> {
fn = null,
name = null;
- get field => '()';
+ String get field => '()';
- get object => _object;
+ dynamic get object => _object;
- set object(value) {
+ void set object(object) {
assert(mode != _MODE_DELETED_);
assert(mode != _MODE_MARKER_);
assert(mode != _MODE_FUNCTION_);
assert(mode != _MODE_PURE_FUNCTION_);
assert(mode != _MODE_PURE_FUNCTION_APPLY_);
- _object = value;
+ _object = object;
- if (value == null) {
+ if (object == null) {
mode = _MODE_NULL_;
- } else {
- if (value is Map) {
- mode = _MODE_MAP_CLOSURE_;
- } else {
- if (_fieldGetterFactory.isMethod(value, name)) {
- mode = _fieldGetterFactory.isMethodInvoke ? _MODE_METHOD_INVOKE_ : _MODE_METHOD_;
- fn = _fieldGetterFactory.method(value, name);
- } else {
- mode = _MODE_FIELD_CLOSURE_;
- fn = _fieldGetterFactory.getter(value, name);
- }
+ return;
+ }
+
+ if (object is Map) {
+ mode = _MODE_MAP_CLOSURE_;
+ return;
+ }
+
+ if (object is ContextLocals) {
+ var ctx = object as ContextLocals;
+ if (ctx.hasProperty(name)) {
+ mode = _MODE_MAP_CLOSURE_;
+ return;
}
+ object = ctx.rootContext;
}
+
+ if (_fieldGetterFactory.isMethod(object, name)) {
+ mode = _fieldGetterFactory.isMethodInvoke ? _MODE_METHOD_INVOKE_ : _MODE_METHOD_;
+ fn = _fieldGetterFactory.method(object, name);
+ return;
+ }
+
+ mode = _MODE_FIELD_CLOSURE_;
+ fn = _fieldGetterFactory.getter(object, name);
}
bool check() {
diff --git a/lib/core/annotation.dart b/lib/core/annotation.dart
index 768def9aa..dcd2f23fd 100644
--- a/lib/core/annotation.dart
+++ b/lib/core/annotation.dart
@@ -15,7 +15,6 @@ export "package:angular/core/annotation_src.dart" show
Directive,
Component,
- Controller,
Decorator,
DirectiveAnnotation,
diff --git a/lib/core/annotation_src.dart b/lib/core/annotation_src.dart
index 643339ca9..b6369324d 100644
--- a/lib/core/annotation_src.dart
+++ b/lib/core/annotation_src.dart
@@ -34,7 +34,7 @@ class Injectable {
}
/**
- * Abstract supper class of [Controller], [Component], and [Decorator].
+ * Abstract supper class of [Component], and [Decorator].
*/
abstract class Directive {
@@ -300,14 +300,6 @@ class Component extends Directive {
}
final bool _resetStyleInheritance;
- /**
- * An expression under which the component's controller instance will be
- * published into. This allows the expressions in the template to be referring
- * to controller instance and its properties.
- */
- @deprecated
- final String publishAs;
-
/**
* If set to true, this component will always use shadow DOM.
* If set to false, this component will never use shadow DOM.
@@ -321,7 +313,6 @@ class Component extends Directive {
cssUrl,
applyAuthorStyles,
resetStyleInheritance,
- this.publishAs,
module,
map,
selector,
@@ -351,7 +342,6 @@ class Component extends Directive {
cssUrl: cssUrls,
applyAuthorStyles: applyAuthorStyles,
resetStyleInheritance: resetStyleInheritance,
- publishAs: publishAs,
map: newMap,
module: module,
selector: selector,
@@ -401,62 +391,6 @@ class Decorator extends Directive {
exportExpressionAttrs: exportExpressionAttrs);
}
-/**
- * Meta-data marker 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,
- 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].
*/
diff --git a/lib/core/module.dart b/lib/core/module.dart
index 57b7b2970..6598f7e34 100644
--- a/lib/core/module.dart
+++ b/lib/core/module.dart
@@ -63,8 +63,9 @@ export "package:angular/core/module_internal.dart" show
ExceptionHandler,
Interpolate,
VmTurnZone,
- PrototypeMap,
RootScope,
+ ClosureMapLocalsAware,
+ ContextLocals,
Scope,
ScopeDigestTTL,
ScopeEvent,
diff --git a/lib/core/parser/dynamic_parser.dart b/lib/core/parser/dynamic_parser.dart
index 041ebb33d..d90bc0e49 100644
--- a/lib/core/parser/dynamic_parser.dart
+++ b/lib/core/parser/dynamic_parser.dart
@@ -12,13 +12,6 @@ import 'package:angular/core/parser/eval.dart';
import 'package:angular/core/parser/utils.dart' show EvalError;
import 'package:angular/utils.dart';
-abstract class ClosureMap {
- Getter lookupGetter(String name);
- Setter lookupSetter(String name);
- Symbol lookupSymbol(String name);
- MethodClosure lookupFunction(String name, CallArguments arguments);
-}
-
@Injectable()
class DynamicParser implements Parser {
final Lexer _lexer;
diff --git a/lib/core/parser/eval_access.dart b/lib/core/parser/eval_access.dart
index 0fdfa61c0..f5abbfbdf 100644
--- a/lib/core/parser/eval_access.dart
+++ b/lib/core/parser/eval_access.dart
@@ -38,22 +38,36 @@ class AccessKeyed extends syntax.AccessKeyed {
* where we have a pair of pre-compiled getter and setter functions that we
* use to do the access the field.
*/
+// todo(vicb) - parser should not depend on ContextLocals
+// todo(vicb) - Map should not be a special case so that we can access the props
abstract class AccessFast {
String get name;
Getter get getter;
Setter get setter;
- _eval(holder) {
+ dynamic _eval(holder) {
if (holder == null) return null;
- return (holder is Map) ? holder[name] : getter(holder);
+ if (holder is Map) return holder[name];
+ if (holder is ContextLocals) {
+ var ctx = holder as ContextLocals;
+ if (ctx.hasProperty(name)) return ctx[name];
+ holder = ctx.rootContext;
+ }
+ return getter(holder);
}
- _assign(scope, holder, value) {
+ dynamic _assign(scope, holder, value) {
if (holder == null) {
_assignToNonExisting(scope, value);
return value;
} else {
- return (holder is Map) ? (holder[name] = value) : setter(holder, value);
+ if (holder is Map) return holder[name] = value;
+ if (holder is ContextLocals) {
+ var ctx = holder as ContextLocals;
+ if (ctx.hasProperty(name)) return ctx[name] = value;
+ holder = ctx.rootContext;
+ }
+ return setter(holder, value);
}
}
diff --git a/lib/core/parser/parser.dart b/lib/core/parser/parser.dart
index 0f9839cd0..0fb5b62f0 100644
--- a/lib/core/parser/parser.dart
+++ b/lib/core/parser/parser.dart
@@ -67,3 +67,10 @@ abstract class ParserBackend {
T newLiteralNumber(num value) => newLiteralPrimitive(value);
T newLiteralString(String value) => null;
}
+
+abstract class ClosureMap {
+ Getter lookupGetter(String name);
+ Setter lookupSetter(String name);
+ Symbol lookupSymbol(String name);
+ MethodClosure lookupFunction(String name, CallArguments arguments);
+}
diff --git a/lib/core/parser/parser_dynamic.dart b/lib/core/parser/parser_dynamic.dart
index 967db7bed..976f6e550 100644
--- a/lib/core/parser/parser_dynamic.dart
+++ b/lib/core/parser/parser_dynamic.dart
@@ -6,23 +6,26 @@ import 'package:angular/core/parser/parser.dart';
class DynamicClosureMap implements ClosureMap {
final Map symbols = {};
+
Getter lookupGetter(String name) {
- var symbol = new Symbol(name);
+ var symbol;
return (o) {
if (o is Map) {
return o[name];
} else {
+ if (symbol == null) symbol = lookupSymbol(name);
return reflect(o).getField(symbol).reflectee;
}
};
}
Setter lookupSetter(String name) {
- var symbol = new Symbol(name);
+ var symbol;
return (o, value) {
if (o is Map) {
return o[name] = value;
} else {
+ if (symbol == null) symbol = lookupSymbol(name);
reflect(o).setField(symbol, value);
return value;
}
@@ -30,13 +33,9 @@ class DynamicClosureMap implements ClosureMap {
}
MethodClosure lookupFunction(String name, CallArguments arguments) {
- var symbol = new Symbol(name);
+ var symbol;
return (o, posArgs, namedArgs) {
- var sNamedArgs = {};
- namedArgs.forEach((name, value) {
- var symbol = symbols.putIfAbsent(name, () => new Symbol(name));
- sNamedArgs[symbol] = value;
- });
+ var sNamedArgs = new Map.fromIterables(namedArgs.keys.map(lookupSymbol), namedArgs.values);
if (o is Map) {
var fn = o[name];
if (fn is Function) {
@@ -46,6 +45,7 @@ class DynamicClosureMap implements ClosureMap {
}
} else {
try {
+ if (symbol == null) symbol = lookupSymbol(name);
return reflect(o).invoke(symbol, posArgs, sNamedArgs).reflectee;
} on NoSuchMethodError catch (e) {
throw 'Undefined function $name';
@@ -54,5 +54,5 @@ class DynamicClosureMap implements ClosureMap {
};
}
- Symbol lookupSymbol(String name) => new Symbol(name);
+ Symbol lookupSymbol(String name) => symbols.putIfAbsent(name, () => new Symbol(name));
}
diff --git a/lib/core/registry_dynamic.dart b/lib/core/registry_dynamic.dart
index 54ea6573d..7306138cc 100644
--- a/lib/core/registry_dynamic.dart
+++ b/lib/core/registry_dynamic.dart
@@ -3,9 +3,7 @@ library angular.core_dynamic;
import 'dart:mirrors';
import 'package:angular/core/annotation_src.dart';
import 'package:angular/core/registry.dart';
-
-export 'package:angular/core/registry.dart' show
- MetadataExtractor;
+export 'package:angular/core/registry.dart' show MetadataExtractor;
var _fieldMetadataCache = new Map>();
diff --git a/lib/core/scope.dart b/lib/core/scope.dart
index 6f3cef32b..11ca4904c 100644
--- a/lib/core/scope.dart
+++ b/lib/core/scope.dart
@@ -84,46 +84,6 @@ class ScopeDigestTTL {
ScopeDigestTTL.value(this.ttl);
}
-//TODO(misko): I don't think this should be in scope.
-class ScopeLocals implements Map {
- static wrapper(scope, Map locals) =>
- new ScopeLocals(scope, locals);
-
- Map _scope;
- Map _locals;
-
- ScopeLocals(this._scope, this._locals);
-
- void operator []=(String name, value) {
- _scope[name] = value;
- }
- dynamic operator [](String name) {
- // Map needed to clear Dart2js warning
- Map map = _locals.containsKey(name) ? _locals : _scope;
- return map[name];
- }
-
- bool get isEmpty => _scope.isEmpty && _locals.isEmpty;
- bool get isNotEmpty => _scope.isNotEmpty || _locals.isNotEmpty;
- List get keys => _scope.keys;
- List get values => _scope.values;
- int get length => _scope.length;
-
- void forEach(fn) {
- _scope.forEach(fn);
- }
- dynamic remove(key) => _scope.remove(key);
- void clear() {
- _scope.clear;
- }
- bool containsKey(key) => _scope.containsKey(key);
- bool containsValue(key) => _scope.containsValue(key);
- void addAll(map) {
- _scope.addAll(map);
- }
- dynamic putIfAbsent(key, fn) => _scope.putIfAbsent(key, fn);
-}
-
/**
* [Scope] is represents a collection of [watch]es [observe]ers, and [context]
* for the watchers, observers and [eval]uations. Scopes structure loosely
@@ -140,6 +100,9 @@ class Scope {
*/
final context;
+ bool hasLocal(name) => context is ContextLocals && (context as ContextLocals).hasProperty(name);
+ dynamic getLocal(name) => (context as ContextLocals)[name];
+
/**
* The [RootScope] of the application.
*/
@@ -184,7 +147,7 @@ class Scope {
/// Do not use. Exposes internal state for testing.
bool get hasOwnStreams => _streams != null && _streams._scope == this;
- Scope(Object this.context, this.rootScope, this._parentScope,
+ Scope(this.context, this.rootScope, this._parentScope,
this._readWriteGroup, this._readOnlyGroup, this.id,
this._stats);
@@ -206,7 +169,7 @@ class Scope {
* by reference. When watching a collection, the reaction function receives a
* [CollectionChangeItem] that lists all the changes.
*/
- Watch watch(String expression, ReactionFn reactionFn, {context,
+ Watch watch(String expression, ReactionFn reactionFn, {context,
FormatterMap formatters, bool canChangeModel: true, bool collection: false}) {
assert(isAttached);
assert(expression is String);
@@ -246,8 +209,8 @@ class Scope {
expression is String ||
expression is Function);
if (expression is String && expression.isNotEmpty) {
- var obj = locals == null ? context : new ScopeLocals(context, locals);
- return rootScope._parser(expression).eval(obj);
+ var ctx = locals == null ? context : new ContextLocals(context, locals);
+ return rootScope._parser(expression).eval(ctx);
}
assert(locals == null);
@@ -323,7 +286,7 @@ class Scope {
_parentScope = null;
}
- _assertInternalStateConsistency() {
+ void _assertInternalStateConsistency() {
assert((() {
rootScope._verifyStreams(null, '', []);
return true;
@@ -360,7 +323,7 @@ class Scope {
}
}
-_mapEqual(Map a, Map b) => a.length == b.length &&
+bool _mapEqual(Map a, Map b) => a.length == b.length &&
a.keys.every((k) => b.containsKey(k) && a[k] == b[k]);
/**
@@ -376,11 +339,11 @@ class ScopeStats {
final evalStopwatch = new AvgStopwatch();
final processStopwatch = new AvgStopwatch();
- List _digestLoopTimes = [];
+ final _digestLoopTimes = [];
int _flushPhaseDuration = 0 ;
int _assertFlushPhaseDuration = 0;
- int _loopNo = 0;
+ int _loopNo;
ScopeStatsEmitter _emitter;
ScopeStatsConfig _config;
@@ -390,7 +353,7 @@ class ScopeStats {
ScopeStats(this._emitter, this._config);
void digestStart() {
- _digestLoopTimes = [];
+ _digestLoopTimes.clear();
_stopwatchReset();
_loopNo = 0;
}
@@ -401,7 +364,7 @@ class ScopeStats {
processStopwatch.elapsedMicroseconds;
}
- _stopwatchReset() {
+ void _stopwatchReset() {
fieldStopwatch.reset();
evalStopwatch.reset();
processStopwatch.reset();
@@ -410,10 +373,9 @@ class ScopeStats {
void digestLoop(int changeCount) {
_loopNo++;
if (_config.emit && _emitter != null) {
- _emitter.emit(_loopNo.toString(), fieldStopwatch, evalStopwatch,
- processStopwatch);
+ _emitter.emit(_loopNo.toString(), fieldStopwatch, evalStopwatch, processStopwatch);
}
- _digestLoopTimes.add( _allStagesDuration() );
+ _digestLoopTimes.add(_allStagesDuration());
_stopwatchReset();
}
@@ -424,23 +386,25 @@ class ScopeStats {
void domWriteEnd() {}
void domReadStart() {}
void domReadEnd() {}
+
void flushStart() {
_stopwatchReset();
}
+
void flushEnd() {
if (_config.emit && _emitter != null) {
- _emitter.emit(RootScope.STATE_FLUSH, fieldStopwatch, evalStopwatch,
- processStopwatch);
+ _emitter.emit(RootScope.STATE_FLUSH, fieldStopwatch, evalStopwatch, processStopwatch);
}
_flushPhaseDuration = _allStagesDuration();
}
+
void flushAssertStart() {
_stopwatchReset();
}
+
void flushAssertEnd() {
if (_config.emit && _emitter != null) {
- _emitter.emit(RootScope.STATE_FLUSH_ASSERT, fieldStopwatch, evalStopwatch,
- processStopwatch);
+ _emitter.emit(RootScope.STATE_FLUSH_ASSERT, fieldStopwatch, evalStopwatch, processStopwatch);
}
_assertFlushPhaseDuration = _allStagesDuration();
}
@@ -466,9 +430,9 @@ class ScopeStatsEmitter {
static pad(String str, int size) => _PAD_.substring(0, max(size - str.length, 0)) + str;
- _ms(num value) => '${pad(_nfDec.format(value), 9)} ms';
- _us(num value) => _ms(value / 1000);
- _tally(num value) => '${pad(_nfInt.format(value), 6)}';
+ String _ms(num value) => '${pad(_nfDec.format(value), 9)} ms';
+ String _us(num value) => _ms(value / 1000);
+ String _tally(num value) => '${pad(_nfInt.format(value), 6)}';
/**
* Emit a message based on the phase and state of stopwatches.
@@ -492,9 +456,8 @@ class ScopeStatsEmitter {
return (prefix == '1' ? _HEADER_ : '') + ' #$prefix:';
}
- String _stat(AvgStopwatch s) {
- return '${_tally(s.count)} / ${_us(s.elapsedMicroseconds)} @(${_tally(s.ratePerMs)} #/ms)';
- }
+ String _stat(AvgStopwatch s) =>
+ '${_tally(s.count)} / ${_us(s.elapsedMicroseconds)} @(${_tally(s.ratePerMs)} #/ms)';
}
/**
@@ -505,6 +468,7 @@ class ScopeStatsConfig {
var emit = false;
ScopeStatsConfig();
+
ScopeStatsConfig.enabled() {
emit = true;
}
@@ -570,8 +534,8 @@ class RootScope extends Scope {
* followed by change detection
* on non-DOM listeners. Any changes detected are process using the reaction function. The digest
* phase is repeated as long as at least one change has been detected. By default, after 5
- * iterations the model is considered unstable and angular exists with an exception. (See
- * ScopeDigestTTL)
+ * iterations the model is considered unstable and angular exits with an exception. (See
+ * [ScopeDigestTTL])
*
* ##flush
*
@@ -1072,7 +1036,6 @@ class ExpressionVisitor implements syntax.Visitor {
final ClosureMap _closureMap;
AST contextRef = scopeContextRef;
-
ExpressionVisitor(this._closureMap);
AST ast;
@@ -1091,8 +1054,7 @@ class ExpressionVisitor implements syntax.Visitor {
AST visitCollection(syntax.Expression exp) => new CollectionAST(visit(exp));
AST _mapToAst(syntax.Expression expression) => visit(expression);
- List _toAst(List expressions) =>
- expressions.map(_mapToAst).toList();
+ List _toAst(List expressions) => expressions.map(_mapToAst).toList();
Map _toAstMap(Map expressions) {
if (expressions.isEmpty) return const {};
@@ -1113,37 +1075,47 @@ class ExpressionVisitor implements syntax.Visitor {
Map named = _toAstMap(exp.arguments.named);
ast = new MethodAST(visit(exp.object), exp.name, positionals, named);
}
+
void visitAccessScope(syntax.AccessScope exp) {
ast = new FieldReadAST(contextRef, exp.name);
}
+
void visitAccessMember(syntax.AccessMember exp) {
ast = new FieldReadAST(visit(exp.object), exp.name);
}
+
void visitBinary(syntax.Binary exp) {
ast = new PureFunctionAST(exp.operation,
_operationToFunction(exp.operation),
[visit(exp.left), visit(exp.right)]);
}
+
+
void visitPrefix(syntax.Prefix exp) {
ast = new PureFunctionAST(exp.operation,
_operationToFunction(exp.operation),
[visit(exp.expression)]);
}
+
void visitConditional(syntax.Conditional exp) {
ast = new PureFunctionAST('?:', _operation_ternary,
[visit(exp.condition), visit(exp.yes),
visit(exp.no)]);
}
+
void visitAccessKeyed(syntax.AccessKeyed exp) {
ast = new ClosureAST('[]', _operation_bracket,
[visit(exp.object), visit(exp.key)]);
}
+
void visitLiteralPrimitive(syntax.LiteralPrimitive exp) {
ast = new ConstantAST(exp.value);
}
+
void visitLiteralString(syntax.LiteralString exp) {
ast = new ConstantAST(exp.value);
}
+
void visitLiteralArray(syntax.LiteralArray exp) {
List items = _toAst(exp.elements);
ast = new PureFunctionAST('[${items.join(', ')}]', new ArrayFn(), items);
@@ -1175,15 +1147,19 @@ class ExpressionVisitor implements syntax.Visitor {
void visitCallFunction(syntax.CallFunction exp) {
_notSupported("function's returing functions");
}
+
void visitAssign(syntax.Assign exp) {
_notSupported('assignement');
}
+
void visitLiteral(syntax.Literal exp) {
_notSupported('literal');
}
+
void visitExpression(syntax.Expression exp) {
_notSupported('?');
}
+
void visitChain(syntax.Chain exp) {
_notSupported(';');
}
@@ -1287,3 +1263,46 @@ class _FormatterWrapper extends FunctionApply {
return value;
}
}
+
+class ClosureMapLocalsAware implements ClosureMap {
+ final ClosureMap wrappedClsMap;
+
+ ClosureMapLocalsAware(this.wrappedClsMap);
+
+ Getter lookupGetter(String name) {
+ var getter;
+ return (o) {
+ if (o is ContextLocals) {
+ var ctx = o as ContextLocals;
+ if (ctx.hasProperty(name)) return ctx[name];
+ o = ctx.rootContext;
+ }
+ if (getter == null) getter = wrappedClsMap.lookupGetter(name);
+ return getter(o);
+ };
+ }
+
+ Setter lookupSetter(String name) {
+ var setter = wrappedClsMap.lookupSetter(name);
+ return (o, value) {
+ return o is ContextLocals ?
+ setter(o.rootContext, value) :
+ setter(o, value);
+ };
+ }
+
+ MethodClosure lookupFunction(String name, CallArguments arguments) {
+ var fn = wrappedClsMap.lookupFunction(name, arguments);
+ return (o, pArgs, nArgs) {
+ if (o is ContextLocals) {
+ var ctx = o as ContextLocals;
+ if (ctx.hasProperty(name)) return fn({name: ctx[name]}, pArgs, nArgs);
+ o = ctx.rootContext;
+ }
+ return fn(o, pArgs, nArgs);
+ };
+ }
+
+ Symbol lookupSymbol(String name) => wrappedClsMap.lookupSymbol(name);
+}
+
diff --git a/lib/core_dom/common.dart b/lib/core_dom/common.dart
index a60a050ee..c99f2da53 100644
--- a/lib/core_dom/common.dart
+++ b/lib/core_dom/common.dart
@@ -39,7 +39,8 @@ Injector forceNewDirectivesAndFormatters(Injector injector, List modules
modules.add(new Module()
..bind(Scope, toFactory: (i) {
var scope = i.parent.get(Scope);
- return scope.createChild(new PrototypeMap(scope.context));
+ //todo(vicb)
+ return scope.createChild(scope.context);
}));
return injector.createChild(modules,
diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart
index bc562c675..3a79d3251 100644
--- a/lib/core_dom/element_binder.dart
+++ b/lib/core_dom/element_binder.dart
@@ -24,14 +24,12 @@ class TemplateElementBinder extends ElementBinder {
String toString() => "[TemplateElementBinder template:$template]";
- _registerViewFactory(node, parentInjector, nodeModule) {
+ void _registerViewFactory(node, parentInjector, nodeModule) {
assert(templateViewFactory != null);
nodeModule
- ..bind(ViewPort, toFactory: (_) =>
- new ViewPort(node, parentInjector.get(Animate)))
- ..bind(ViewFactory, toValue: templateViewFactory)
- ..bind(BoundViewFactory, toFactory: (Injector injector) =>
- templateViewFactory.bind(injector));
+ ..bind(ViewPort, toFactory: (_) => new ViewPort(node, parentInjector.get(Animate)))
+ ..bind(ViewFactory, toValue: templateViewFactory)
+ ..bind(BoundViewFactory, toFactory: (Injector inj) => templateViewFactory.bind(inj));
}
}
@@ -80,10 +78,9 @@ class ElementBinder {
return _directiveCache = decorators;
}
- bool get hasDirectivesOrEvents =>
- _usableDirectiveRefs.isNotEmpty || onEvents.isNotEmpty;
+ bool get hasDirectivesOrEvents => _usableDirectiveRefs.isNotEmpty || onEvents.isNotEmpty;
- _bindTwoWay(tasks, expression, scope, dstPathFn, controller, formatters, dstExpression) {
+ void _bindTwoWay(tasks, expression, scope, dstPathFn, controller, formatters, dstExpression) {
var taskId = tasks.registerTask();
Expression expressionFn = _parser(expression);
@@ -110,7 +107,7 @@ class ElementBinder {
}
}
- _bindOneWay(tasks, expression, scope, dstPathFn, controller, formatters) {
+ void _bindOneWay(tasks, expression, scope, dstPathFn, controller, formatters) {
var taskId = tasks.registerTask();
Expression attrExprFn = _parser(expression);
@@ -120,11 +117,12 @@ class ElementBinder {
}, formatters: formatters);
}
- _bindCallback(dstPathFn, controller, expression, scope) {
- dstPathFn.assign(controller, _parser(expression).bind(scope.context, ScopeLocals.wrapper));
+ void _bindCallback(dstPathFn, controller, expression, scope) {
+ dstPathFn.assign(controller, _parser(expression).bind(scope.context, ContextLocals.wrapper));
}
- _createAttrMappings(controller, scope, List mappings, nodeAttrs, formatters, tasks) {
+ void _createAttrMappings(controller, scope, List mappings, nodeAttrs,
+ formatters, tasks) {
mappings.forEach((MappingParts p) {
var attrName = p.attrName;
var dstExpression = p.dstExpression;
@@ -139,8 +137,7 @@ class ElementBinder {
var bindAttr = bindAttrs["bind-${p.attrName}"];
if (bindAttr != null) {
if (p.mode == '<=>') {
- _bindTwoWay(tasks, bindAttr, scope, dstPathFn,
- controller, formatters, dstExpression);
+ _bindTwoWay(tasks, bindAttr, scope, dstPathFn, controller, formatters, dstExpression);
} else if(p.mode == '&') {
_bindCallback(dstPathFn, controller, bindAttr, scope);
} else {
@@ -167,8 +164,7 @@ class ElementBinder {
case '=>': // one-way
if (nodeAttrs[attrName] == null) return;
- _bindOneWay(tasks, nodeAttrs[attrName], scope,
- dstPathFn, controller, formatters);
+ _bindOneWay(tasks, nodeAttrs[attrName], scope, dstPathFn, controller, formatters);
break;
case '=>!': // one-way, one-time
@@ -190,7 +186,7 @@ class ElementBinder {
});
}
- _link(nodeInjector, probe, scope, nodeAttrs, formatters) {
+ void _link(nodeInjector, probe, scope, nodeAttrs, formatters) {
_usableDirectiveRefs.forEach((DirectiveRef ref) {
var linkTimer;
try {
@@ -200,13 +196,9 @@ class ElementBinder {
probe.directives.add(controller);
assert((linkMapTimer = _perf.startTimer('ng.view.link.map', ref.type)) != false);
- if (ref.annotation is Controller) {
- scope.context[(ref.annotation as Controller).publishAs] = controller;
- }
-
- var tasks = new _TaskList(controller is AttachAware ? () {
- if (scope.isAttached) controller.attach();
- } : null);
+ var tasks = new _TaskList(controller is AttachAware ?
+ () {if (scope.isAttached) controller.attach();} :
+ null);
if (ref.mappings.isNotEmpty) {
if (nodeAttrs == null) nodeAttrs = new _AnchorAttrs(ref);
@@ -236,8 +228,8 @@ class ElementBinder {
});
}
- _createDirectiveFactories(DirectiveRef ref, nodeModule, node, nodesAttrsDirectives, nodeAttrs,
- visibility) {
+ void _createDirectiveFactories(DirectiveRef ref, nodeModule, node, nodesAttrsDirectives,
+ nodeAttrs, visibility) {
if (ref.type == TextMustache) {
nodeModule.bind(TextMustache, toFactory: (Injector injector) {
return new TextMustache(node, ref.value, injector.get(Interpolate),
@@ -272,7 +264,7 @@ class ElementBinder {
}
// Overridden in TemplateElementBinder
- _registerViewFactory(node, parentInjector, nodeModule) {
+ void _registerViewFactory(node, parentInjector, nodeModule) {
nodeModule..bind(ViewPort, toValue: null)
..bind(ViewFactory, toValue: null)
..bind(BoundViewFactory, toValue: null);
@@ -302,16 +294,11 @@ class ElementBinder {
directiveRefs.forEach((DirectiveRef ref) {
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);
- }
_createDirectiveFactories(ref, nodeModule, node, nodesAttrsDirectives, nodeAttrs,
- visibility);
- if (ref.annotation.module != null) {
- nodeModule.install(ref.annotation.module());
+ annotation.visibility);
+ if (annotation.module != null) {
+ nodeModule.install(annotation.module());
}
});
diff --git a/lib/core_dom/event_handler.dart b/lib/core_dom/event_handler.dart
index 1a0f5d9b5..f6dfadd74 100644
--- a/lib/core_dom/event_handler.dart
+++ b/lib/core_dom/event_handler.dart
@@ -4,7 +4,7 @@ typedef void EventFunction(event);
/**
* [EventHandler] is responsible for handling events bound using on-* syntax
- * (i.e. `on-click="ctrl.doSomething();"`). The root of the application has an
+ * (i.e. `on-click="doSomething();"`). The root of the application has an
* EventHandler attached as does every [Component].
*
* Events bound within [Component] are handled by EventHandler attached to
@@ -16,12 +16,14 @@ typedef void EventFunction(event);
* Example:
*
*
- * Button;
+ * Button;
*
*
- * @Component(selector: '[foo]', publishAs: ctrl)
- * class FooController {
- * say(String something) => print(something);
+ * @Component(selector: '[foo]')
+ * class FooComponent {
+ * void say(String something) {
+ * print(something);
+ * }
* }
*
* When button is clicked, "Hello" will be printed in the console.
@@ -48,31 +50,25 @@ class EventHandler {
}
void _eventListener(dom.Event event) {
- dom.Node element = event.target;
- while (element != null && element != _rootNode) {
+ for (dom.Node el = event.target; el != null && el != _rootNode; el = el.parentNode) {
var expression;
- if (element is dom.Element)
- expression = (element as dom.Element).attributes[eventNameToAttrName(event.type)];
+ if (el is dom.Element)
+ expression = (el as dom.Element).attributes[eventNameToAttrName(event.type)];
if (expression != null) {
try {
- var scope = _getScope(element);
+ var scope = _getScope(el);
if (scope != null) scope.eval(expression);
} catch (e, s) {
_exceptionHandler(e, s);
}
}
- element = element.parentNode;
}
}
- Scope _getScope(dom.Node element) {
- // var topElement = (rootNode is dom.ShadowRoot) ? rootNode.parentNode : rootNode;
- while (element != _rootNode.parentNode) {
- ElementProbe probe = _expando[element];
- if (probe != null) {
- return probe.scope;
- }
- element = element.parentNode;
+ Scope _getScope(dom.Node el) {
+ for (;el != _rootNode.parentNode; el = el.parentNode) {
+ ElementProbe probe = _expando[el];
+ if (probe != null) return probe.scope;
}
return null;
}
@@ -82,10 +78,10 @@ class EventHandler {
* be transformed into on-some-custom-event.
*/
static String eventNameToAttrName(String eventName) {
- var part = eventName.replaceAllMapped(new RegExp("([A-Z])"), (Match match) {
+ var part = eventName.replaceAllMapped(new RegExp('([A-Z])'), (Match match) {
return '-${match.group(0).toLowerCase()}';
});
- return 'on-${part}';
+ return 'on-$part';
}
/**
@@ -93,18 +89,17 @@ class EventHandler {
* corresponds to event named 'someCustomEvent'.
*/
static String attrNameToEventName(String attrName) {
- var part = attrName.startsWith("on-") ? attrName.substring(3) : attrName;
+ var part = attrName.startsWith('on-') ? attrName.substring(3) : attrName;
part = part.replaceAllMapped(new RegExp(r'\-(\w)'), (Match match) {
return match.group(0).toUpperCase();
});
- return part.replaceAll("-", "");
+ return part.replaceAll('-', '');
}
}
@Injectable()
class ShadowRootEventHandler extends EventHandler {
- ShadowRootEventHandler(dom.ShadowRoot shadowRoot,
- Expando expando,
+ ShadowRootEventHandler(dom.ShadowRoot shadowRoot, Expando expando,
ExceptionHandler exceptionHandler)
: super(shadowRoot, expando, exceptionHandler);
}
diff --git a/lib/core_dom/module_internal.dart b/lib/core_dom/module_internal.dart
index decce9591..21c32972d 100644
--- a/lib/core_dom/module_internal.dart
+++ b/lib/core_dom/module_internal.dart
@@ -13,7 +13,8 @@ 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/change_detection/watch_group.dart' show Watch, PrototypeMap;
+// todo vicb
+import 'package:angular/change_detection/watch_group.dart' show Watch, ContextLocals;
import 'package:angular/core/registry.dart';
import 'package:angular/directive/module.dart' show NgBaseCss;
@@ -62,7 +63,7 @@ class CoreDomModule extends Module {
bind(TranscludingComponentFactory);
bind(Content);
bind(ContentPort, toValue: null);
-
+
bind(Http);
bind(UrlRewriter);
bind(HttpBackend);
diff --git a/lib/core_dom/shadow_dom_component_factory.dart b/lib/core_dom/shadow_dom_component_factory.dart
index 5782f96d1..33c4d6c41 100644
--- a/lib/core_dom/shadow_dom_component_factory.dart
+++ b/lib/core_dom/shadow_dom_component_factory.dart
@@ -1,5 +1,10 @@
part of angular.core.dom_internal;
+/**
+ * [ComponentFactory] is responsible for setting up components. This includes
+ * the shadowDom, fetching template, importing styles, setting up attribute
+ * mappings, publishing the controller, and compiling and caching the template.
+ */
abstract class ComponentFactory {
FactoryFn call(dom.Node node, DirectiveRef ref);
@@ -35,32 +40,22 @@ class ShadowDomComponentFactory implements ComponentFactory {
FactoryFn call(dom.Node node, DirectiveRef ref) {
return (Injector injector) {
var component = ref.annotation as Component;
- Scope scope = injector.get(Scope);
- ViewCache viewCache = injector.get(ViewCache);
- Http http = injector.get(Http);
- TemplateCache templateCache = injector.get(TemplateCache);
- DirectiveMap directives = injector.get(DirectiveMap);
- NgBaseCss baseCss = injector.get(NgBaseCss);
+ final scope = injector.get(Scope);
+ final viewCache = injector.get(ViewCache);
+ final http = injector.get(Http);
+ final templateCache = injector.get(TemplateCache);
+ final directives = injector.get(DirectiveMap);
+ final baseCss = injector.get(NgBaseCss);
// This is a bit of a hack since we are returning different type then we are.
- var componentFactory = new _ComponentFactory(node, ref.type, component,
+ var componentFactory = new _ShadowDomComponentFactory(node, ref.type, component,
injector.get(dom.NodeTreeSanitizer), _expando, baseCss, _styleElementCache);
- var controller = componentFactory.call(injector, scope, viewCache, http, templateCache,
- directives);
- componentFactory.shadowScope.context[component.publishAs] = controller;
- return controller;
+ return componentFactory.call(injector, scope, viewCache, http, templateCache, directives);
};
}
}
-
-/**
- * ComponentFactory is responsible for setting up components. This includes
- * the shadowDom, fetching template, importing styles, setting up attribute
- * mappings, publishing the controller, and compiling and caching the template.
- */
-class _ComponentFactory implements Function {
-
+class _ShadowDomComponentFactory implements Function {
final dom.Element element;
final Type type;
final Component component;
@@ -74,17 +69,16 @@ class _ComponentFactory implements Function {
Injector shadowInjector;
var controller;
- _ComponentFactory(this.element, this.type, this.component, this.treeSanitizer,
- this._expando, this._baseCss, this._styleElementCache);
+ _ShadowDomComponentFactory(this.element, this.type, this.component, this.treeSanitizer,
+ this._expando, this._baseCss, this._styleElementCache);
dynamic call(Injector injector, Scope scope,
ViewCache viewCache, Http http, TemplateCache templateCache,
DirectiveMap directives) {
shadowDom = element.createShadowRoot()
- ..applyAuthorStyles = component.applyAuthorStyles
- ..resetStyleInheritance = component.resetStyleInheritance;
+ ..applyAuthorStyles = component.applyAuthorStyles
+ ..resetStyleInheritance = component.resetStyleInheritance;
- shadowScope = scope.createChild({}); // Isolate
// TODO(pavelgj): fetching CSS with Http is mainly an attempt to
// work around an unfiled Chrome bug when reloading same CSS breaks
// styles all over the page. We shouldn't be doing browsers work,
@@ -117,7 +111,9 @@ class _ComponentFactory implements Function {
}
return shadowDom;
}));
- controller = createShadowInjector(injector, templateLoader).get(type);
+
+ var shadowInjector = createShadowInjector(scope, injector, templateLoader);
+ var controller = shadowInjector.get(type);
ComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
return controller;
}
@@ -128,19 +124,23 @@ class _ComponentFactory implements Function {
return shadowDom;
}
- Injector createShadowInjector(injector, TemplateLoader templateLoader) {
+ Injector createShadowInjector(Scope scope, Injector injector, TemplateLoader templateLoader) {
var probe;
var shadowModule = new Module()
- ..bind(type)
- ..bind(NgElement)
- ..bind(EventHandler, toImplementation: ShadowRootEventHandler)
- ..bind(Scope, toValue: shadowScope)
- ..bind(TemplateLoader, toValue: templateLoader)
- ..bind(dom.ShadowRoot, toValue: shadowDom)
- ..bind(ElementProbe, toFactory: (_) => probe);
+ ..bind(type)
+ ..bind(NgElement)
+ ..bind(EventHandler, toImplementation: ShadowRootEventHandler)
+ ..bind(Scope, toFactory: (inj) => scope.createChild(inj.get(type)))
+ ..bind(TemplateLoader, toValue: templateLoader)
+ ..bind(dom.ShadowRoot, toValue: shadowDom)
+ ..bind(ElementProbe, toFactory: (_) => probe);
+
shadowInjector = injector.createChild([shadowModule], name: SHADOW_DOM_INJECTOR_NAME);
+ shadowScope = shadowInjector.get(Scope);
+
probe = _expando[shadowDom] = new ElementProbe(
injector.get(ElementProbe), shadowDom, shadowInjector, shadowScope);
+
return shadowInjector;
}
}
diff --git a/lib/core_dom/tagging_compiler.dart b/lib/core_dom/tagging_compiler.dart
index a03007428..8f9b1579a 100644
--- a/lib/core_dom/tagging_compiler.dart
+++ b/lib/core_dom/tagging_compiler.dart
@@ -19,7 +19,7 @@ class TaggingCompiler implements Compiler {
List elementBinders) {
var node = domCursor.current;
- if (node.nodeType == 1) {
+ if (node.nodeType == dom.Node.ELEMENT_NODE) {
// If nodetype is a element, call selector matchElement.
// If text, call selector.matchText
diff --git a/lib/core_dom/transcluding_component_factory.dart b/lib/core_dom/transcluding_component_factory.dart
index 008286c34..2082d4b46 100644
--- a/lib/core_dom/transcluding_component_factory.dart
+++ b/lib/core_dom/transcluding_component_factory.dart
@@ -1,7 +1,6 @@
part of angular.core.dom_internal;
-@Decorator(
- selector: 'content')
+@Decorator(selector: 'content')
class Content implements AttachAware, DetachAware {
final ContentPort _port;
final dom.Element _element;
@@ -12,7 +11,7 @@ class Content implements AttachAware, DetachAware {
if (_port == null) return;
_beginComment = _port.content(_element);
}
-
+
void detach() {
if (_port == null) return;
_port.detachContent(_beginComment);
@@ -21,16 +20,16 @@ class Content implements AttachAware, DetachAware {
class ContentPort {
dom.Element _element;
- var _childNodes = [];
+ final _childNodes = [];
ContentPort(this._element);
void pullNodes() {
_childNodes.addAll(_element.nodes);
- _element.nodes = [];
+ _element.nodes.clear();
}
- content(dom.Element elt) {
+ dom.Comment content(dom.Element elt) {
var hash = elt.hashCode;
var beginComment = new dom.Comment("content $hash");
@@ -38,7 +37,7 @@ class ContentPort {
elt.parent.insertBefore(beginComment, elt);
elt.parent.insertAllBefore(_childNodes, elt);
elt.parent.insertBefore(new dom.Comment("end-content $hash"), elt);
- _childNodes = [];
+ _childNodes.clear();
}
elt.remove();
return beginComment;
@@ -101,22 +100,20 @@ class TranscludingComponentFactory implements ComponentFactory {
}
TemplateLoader templateLoader = new TemplateLoader(elementFuture);
- Scope shadowScope = scope.createChild({});
-
var probe;
var childModule = new Module()
..bind(ref.type)
..bind(NgElement)
..bind(ContentPort, toValue: contentPort)
- ..bind(Scope, toValue: shadowScope)
+ ..bind(Scope, toFactory: (i) => scope.createChild(i.get(ref.type)))
..bind(TemplateLoader, toValue: templateLoader)
..bind(dom.ShadowRoot, toValue: new ShadowlessShadowRoot(element))
..bind(ElementProbe, toFactory: (_) => probe);
childInjector = injector.createChild([childModule], name: SHADOW_DOM_INJECTOR_NAME);
var controller = childInjector.get(ref.type);
- shadowScope.context[component.publishAs] = controller;
- ComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
+ var childScope = childInjector.get(Scope);
+ ComponentFactory._setupOnShadowDomAttach(controller, templateLoader, childScope);
return controller;
};
}
diff --git a/lib/directive/module.dart b/lib/directive/module.dart
index 37c01c3a6..aea95b368 100644
--- a/lib/directive/module.dart
+++ b/lib/directive/module.dart
@@ -12,7 +12,7 @@
*
* For example:
*
- * this text is conditionally visible
+ * this text is conditionally visible
*
*/
library angular.directive;
diff --git a/lib/directive/ng_bind.dart b/lib/directive/ng_bind.dart
index 04346cb0e..062b0f8c9 100644
--- a/lib/directive/ng_bind.dart
+++ b/lib/directive/ng_bind.dart
@@ -8,7 +8,7 @@ part of angular.directive;
* Typically, you don't use ngBind directly, but instead you use the double
* curly markup like {{ expression }} which is similar but less verbose.
*
- * It is preferrable to use ngBind instead of {{ expression }} when a template
+ * It is preferable to use ngBind instead of {{ expression }} when a template
* is momentarily displayed by the browser in its raw state before Angular
* compiles it. Since ngBind is an element attribute, it makes the bindings
* invisible to the user while the page is loading.
diff --git a/lib/directive/ng_class.dart b/lib/directive/ng_class.dart
index 5fb9052a7..32e1ef11c 100644
--- a/lib/directive/ng_class.dart
+++ b/lib/directive/ng_class.dart
@@ -169,7 +169,8 @@ abstract class _NgClassBase {
nodeAttrs.observe('class', (String cls) {
if (prevCls != cls) {
prevCls = cls;
- _applyChanges(_scope.context[r'$index']);
+ var index = _scope.hasLocal(r'$index') ? _scope.getLocal(r'$index') : null;
+ _applyChanges(index);
}
});
}
@@ -178,7 +179,8 @@ abstract class _NgClassBase {
if (_watchExpression != null) _watchExpression.remove();
_watchExpression = _scope.watch(expression, (v, _) {
_computeChanges(v);
- _applyChanges(_scope.context[r'$index']);
+ var index = _scope.hasLocal(r'$index') ? _scope.getLocal(r'$index') : null;
+ _applyChanges(index);
},
canChangeModel: false,
collection: true);
diff --git a/lib/directive/ng_events.dart b/lib/directive/ng_events.dart
index 14c422d3f..b7fdd10a7 100644
--- a/lib/directive/ng_events.dart
+++ b/lib/directive/ng_events.dart
@@ -134,10 +134,9 @@ class NgEvent {
// object? One would pretty much only assign to one or two of those
// properties. I'm opting for the map since it's less boilerplate code.
var listeners = {};
- final dom.Element element;
- final Scope scope;
+ final dom.Element _element;
- NgEvent(this.element, this.scope);
+ NgEvent(this._element);
// NOTE: Do not use the element.on['some_event'].listen(...) syntax. Doing so
// has two downsides:
@@ -155,55 +154,55 @@ class NgEvent {
}
}
- set onAbort(value) => initListener(element.onAbort, value);
- set onBeforeCopy(value) => initListener(element.onBeforeCopy, value);
- set onBeforeCut(value) => initListener(element.onBeforeCut, value);
- set onBeforePaste(value) => initListener(element.onBeforePaste, value);
- set onBlur(value) => initListener(element.onBlur, value);
- set onChange(value) => initListener(element.onChange, value);
- set onClick(value) => initListener(element.onClick, value);
- set onContextMenu(value) => initListener(element.onContextMenu, value);
- set onCopy(value) => initListener(element.onCopy, value);
- set onCut(value) => initListener(element.onCut, value);
- set onDoubleClick(value) => initListener(element.onDoubleClick, value);
- set onDrag(value) => initListener(element.onDrag, value);
- set onDragEnd(value) => initListener(element.onDragEnd, value);
- set onDragEnter(value) => initListener(element.onDragEnter, value);
- set onDragLeave(value) => initListener(element.onDragLeave, value);
- set onDragOver(value) => initListener(element.onDragOver, value);
- set onDragStart(value) => initListener(element.onDragStart, value);
- set onDrop(value) => initListener(element.onDrop, value);
- set onError(value) => initListener(element.onError, value);
- set onFocus(value) => initListener(element.onFocus, value);
- set onFullscreenChange(value) => initListener(element.onFullscreenChange, value);
- set onFullscreenError(value) => initListener(element.onFullscreenError, value);
- set onInput(value) => initListener(element.onInput, value);
- set onInvalid(value) => initListener(element.onInvalid, value);
- set onKeyDown(value) => initListener(element.onKeyDown, value);
- set onKeyPress(value) => initListener(element.onKeyPress, value);
- set onKeyUp(value) => initListener(element.onKeyUp, value);
- set onLoad(value) => initListener(element.onLoad, value);
- set onMouseDown(value) => initListener(element.onMouseDown, value);
- set onMouseEnter(value) => initListener(element.onMouseEnter, value);
- set onMouseLeave(value) => initListener(element.onMouseLeave, value);
- set onMouseMove(value) => initListener(element.onMouseMove, value);
- set onMouseOut(value) => initListener(element.onMouseOut, value);
- set onMouseOver(value) => initListener(element.onMouseOver, value);
- set onMouseUp(value) => initListener(element.onMouseUp, value);
- set onMouseWheel(value) => initListener(element.onMouseWheel, value);
- set onPaste(value) => initListener(element.onPaste, value);
- set onReset(value) => initListener(element.onReset, value);
- set onScroll(value) => initListener(element.onScroll, value);
- set onSearch(value) => initListener(element.onSearch, value);
- set onSelect(value) => initListener(element.onSelect, value);
- set onSelectStart(value) => initListener(element.onSelectStart, value);
-// set onSpeechChange(value) => initListener(element.onSpeechChange, value);
- set onSubmit(value) => initListener(element.onSubmit, value);
- set onTouchCancel(value) => initListener(element.onTouchCancel, value);
- set onTouchEnd(value) => initListener(element.onTouchEnd, value);
- set onTouchEnter(value) => initListener(element.onTouchEnter, value);
- set onTouchLeave(value) => initListener(element.onTouchLeave, value);
- set onTouchMove(value) => initListener(element.onTouchMove, value);
- set onTouchStart(value) => initListener(element.onTouchStart, value);
- set onTransitionEnd(value) => initListener(element.onTransitionEnd, value);
+ set onAbort(value) => initListener(_element.onAbort, value);
+ set onBeforeCopy(value) => initListener(_element.onBeforeCopy, value);
+ set onBeforeCut(value) => initListener(_element.onBeforeCut, value);
+ set onBeforePaste(value) => initListener(_element.onBeforePaste, value);
+ set onBlur(value) => initListener(_element.onBlur, value);
+ set onChange(value) => initListener(_element.onChange, value);
+ set onClick(value) => initListener(_element.onClick, value);
+ set onContextMenu(value) => initListener(_element.onContextMenu, value);
+ set onCopy(value) => initListener(_element.onCopy, value);
+ set onCut(value) => initListener(_element.onCut, value);
+ set onDoubleClick(value) => initListener(_element.onDoubleClick, value);
+ set onDrag(value) => initListener(_element.onDrag, value);
+ set onDragEnd(value) => initListener(_element.onDragEnd, value);
+ set onDragEnter(value) => initListener(_element.onDragEnter, value);
+ set onDragLeave(value) => initListener(_element.onDragLeave, value);
+ set onDragOver(value) => initListener(_element.onDragOver, value);
+ set onDragStart(value) => initListener(_element.onDragStart, value);
+ set onDrop(value) => initListener(_element.onDrop, value);
+ set onError(value) => initListener(_element.onError, value);
+ set onFocus(value) => initListener(_element.onFocus, value);
+ set onFullscreenChange(value) => initListener(_element.onFullscreenChange, value);
+ set onFullscreenError(value) => initListener(_element.onFullscreenError, value);
+ set onInput(value) => initListener(_element.onInput, value);
+ set onInvalid(value) => initListener(_element.onInvalid, value);
+ set onKeyDown(value) => initListener(_element.onKeyDown, value);
+ set onKeyPress(value) => initListener(_element.onKeyPress, value);
+ set onKeyUp(value) => initListener(_element.onKeyUp, value);
+ set onLoad(value) => initListener(_element.onLoad, value);
+ set onMouseDown(value) => initListener(_element.onMouseDown, value);
+ set onMouseEnter(value) => initListener(_element.onMouseEnter, value);
+ set onMouseLeave(value) => initListener(_element.onMouseLeave, value);
+ set onMouseMove(value) => initListener(_element.onMouseMove, value);
+ set onMouseOut(value) => initListener(_element.onMouseOut, value);
+ set onMouseOver(value) => initListener(_element.onMouseOver, value);
+ set onMouseUp(value) => initListener(_element.onMouseUp, value);
+ set onMouseWheel(value) => initListener(_element.onMouseWheel, value);
+ set onPaste(value) => initListener(_element.onPaste, value);
+ set onReset(value) => initListener(_element.onReset, value);
+ set onScroll(value) => initListener(_element.onScroll, value);
+ set onSearch(value) => initListener(_element.onSearch, value);
+ set onSelect(value) => initListener(_element.onSelect, value);
+ set onSelectStart(value) => initListener(_element.onSelectStart, value);
+// set onSpeechChange(value) => initListener(_element.onSpeechChange, value);
+ set onSubmit(value) => initListener(_element.onSubmit, value);
+ set onTouchCancel(value) => initListener(_element.onTouchCancel, value);
+ set onTouchEnd(value) => initListener(_element.onTouchEnd, value);
+ set onTouchEnter(value) => initListener(_element.onTouchEnter, value);
+ set onTouchLeave(value) => initListener(_element.onTouchLeave, value);
+ set onTouchMove(value) => initListener(_element.onTouchMove, value);
+ set onTouchStart(value) => initListener(_element.onTouchStart, value);
+ set onTransitionEnd(value) => initListener(_element.onTransitionEnd, value);
}
diff --git a/lib/directive/ng_if.dart b/lib/directive/ng_if.dart
index 562bfa181..fd419e09a 100644
--- a/lib/directive/ng_if.dart
+++ b/lib/directive/ng_if.dart
@@ -1,7 +1,7 @@
part of angular.directive;
/**
- * Base class for NgIf and NgUnless.
+ * Base class for [NgIf] and [NgUnless].
*/
abstract class _NgUnlessIfAttrDirectiveBase {
final BoundViewFactory _boundViewFactory;
@@ -10,24 +10,14 @@ abstract class _NgUnlessIfAttrDirectiveBase {
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._boundViewFactory, this._viewPort, this._scope);
// Override in subclass.
void set condition(value);
void _ensureViewExists() {
if (_view == null) {
- _childScope = _scope.createChild(new PrototypeMap(_scope.context));
- _view = _boundViewFactory(_childScope);
+ _view = _boundViewFactory(_scope);
var view = _view;
_scope.rootScope.domWrite(() {
_viewPort.insert(view);
@@ -41,9 +31,7 @@ abstract class _NgUnlessIfAttrDirectiveBase {
_scope.rootScope.domWrite(() {
_viewPort.remove(view);
});
- _childScope.destroy();
_view = null;
- _childScope = null;
}
}
}
diff --git a/lib/directive/ng_include.dart b/lib/directive/ng_include.dart
index be450524f..361d0c959 100644
--- a/lib/directive/ng_include.dart
+++ b/lib/directive/ng_include.dart
@@ -19,43 +19,33 @@ part of angular.directive;
selector: '[ng-include]',
map: const {'ng-include': '@url'})
class NgInclude {
-
- final dom.Element element;
- final Scope scope;
- final ViewCache viewCache;
- final Injector injector;
- final DirectiveMap directives;
+ final dom.Element _element;
+ final Scope _scope;
+ final ViewCache _viewCache;
+ final Injector _injector;
+ 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._injector, this._directives);
- _cleanUp() {
- if (_view == null) return;
+ void set url(String value) {
+ _cleanUp();
+ if (value != null && value != '') {
+ _viewCache.fromUrl(value, _directives).then(_updateContent);
+ }
+ }
+ void _cleanUp() {
+ if (_view == null) return;
_view.nodes.forEach((node) => node.remove);
- _scope.destroy();
- element.innerHtml = '';
-
+ _element.innerHtml = '';
_view = null;
- _scope = null;
}
- _updateContent(createView) {
- // create a new scope
- _scope = scope.createChild(new PrototypeMap(scope.context));
- _view = createView(injector.createChild([new Module()
- ..bind(Scope, toValue: _scope)]));
+ void _updateContent(ViewFactory createView) {
+ _view = createView(new Module()..bind(Scope, toValue: _scope));
_view.nodes.forEach((node) => element.append(node));
}
-
-
- set url(value) {
- _cleanUp();
- if (value != null && value != '') {
- viewCache.fromUrl(value, directives).then(_updateContent);
- }
- }
}
diff --git a/lib/directive/ng_repeat.dart b/lib/directive/ng_repeat.dart
index 99e7d93e9..78464428a 100644
--- a/lib/directive/ng_repeat.dart
+++ b/lib/directive/ng_repeat.dart
@@ -112,8 +112,7 @@ class NgRepeat {
..[r'$index'] = index
..[r'$id'] = (obj) => obj;
if (_keyIdentifier != null) context[_keyIdentifier] = key;
- return relaxFnArgs(trackBy.eval)(new ScopeLocals(_scope.context,
- context));
+ return relaxFnArgs(trackBy.eval)(new ContextLocals(_scope.context, context));
});
}
@@ -151,17 +150,13 @@ class NgRepeat {
var domIndex;
var addRow = (int index, value, View previousView) {
- var childContext = _updateContext(new PrototypeMap(_scope.context), index,
- length)..[_valueIdentifier] = value;
+ // todo vicb
+ var childContext = new ContextLocals(_scope.context);
+ childContext = _updateContext(childContext, index, length);
+ childContext[_valueIdentifier] = value;
var childScope = _scope.createChild(childContext);
var view = _boundViewFactory(childScope);
- var nodes = view.nodes;
- rows[index] = new _Row(_generateId(index, value, index))
- ..view = view
- ..scope = childScope
- ..nodes = nodes
- ..startNode = nodes.first
- ..endNode = nodes.last;
+ rows[index] = new _Row(_generateId(index, value, index), childScope, view);
_viewPort.insert(view, insertAfter: previousView);
};
@@ -195,6 +190,7 @@ class NgRepeat {
var previousRow = _rows[previousIndex];
var childScope = previousRow.scope;
var childContext = _updateContext(childScope.context, index, length);
+ childContext[_valueIdentifier] = value;
if (!identical(childScope.context[_valueIdentifier], value)) {
childContext[_valueIdentifier] = value;
}
@@ -216,8 +212,7 @@ class NgRepeat {
if (changeFn == null) {
rows[targetIndex] = _rows[targetIndex];
domIndex--;
- // The element has not moved but `$last` and `$middle` might still need
- // to be updated
+ // The element has not moved but `$last` and `$middle` might still need to be updated
_updateContext(rows[targetIndex].scope.context, targetIndex, length);
} else {
changeFn(targetIndex, previousView);
@@ -228,9 +223,11 @@ class NgRepeat {
_rows = rows;
}
- PrototypeMap _updateContext(PrototypeMap context, int index, int length) {
- var first = (index == 0);
- var last = (index == length - 1);
+ // todo(vicb): computeLocals
+ ContextLocals _updateContext(ContextLocals context, int index, int len) {
+ var first = index == 0;
+ var last = index == len - 1;
+
return context
..[r'$index'] = index
..[r'$first'] = first
@@ -245,9 +242,6 @@ class _Row {
final id;
Scope scope;
View view;
- dom.Element startNode;
- dom.Element endNode;
- List nodes;
- _Row(this.id);
+ _Row(this.id, this.scope, this.view);
}
diff --git a/lib/directive/ng_switch.dart b/lib/directive/ng_switch.dart
index c9aec1275..3ca0ad378 100644
--- a/lib/directive/ng_switch.dart
+++ b/lib/directive/ng_switch.dart
@@ -1,19 +1,19 @@
part of angular.directive;
/**
- * The ngSwitch directive is used to conditionally swap DOM structure on your
- * template based on a scope expression. Elements within ngSwitch but without
- * ngSwitchWhen or ngSwitchDefault directives will be preserved at the location
+ * The [ngSwitch] directive is used to conditionally swap DOM structure on your
+ * template based on a scope expression. Elements within [ngSwitch] but without
+ * [ngSwitchWhen] or [ngSwitchDefault] directives will be preserved at the location
* as specified in the template.
*
- * The directive itself works similar to ngInclude, however, instead of
- * downloading template code (or loading it from the template cache), ngSwitch
- * simply choses one of the nested elements and makes it visible based on which
+ * The directive itself works similar to [ngInclude], however, instead of
+ * downloading template code (or loading it from the template cache), [ngSwitch]
+ * simply chooses one of the nested elements and makes it visible based on which
* element matches the value obtained from the evaluated expression. In other
* words, you define a container element (where you place the directive), place
* an expression on the **ng-switch="..." attribute**, define any inner elements
* inside of the directive and place a when attribute per element. The when
- * attribute is used to inform ngSwitch which element to display when the on
+ * attribute is used to inform [ngSwitch] which element to display when the on
* expression is evaluated. If a matching expression is not found via a when
* attribute then an element with the default attribute is displayed.
*
@@ -57,56 +57,50 @@ part of angular.directive;
},
visibility: Directive.DIRECT_CHILDREN_VISIBILITY)
class NgSwitch {
- Map> cases = new Map>();
- List<_ViewScopePair> currentViews = <_ViewScopePair>[];
+ final _cases = >{'?': <_Case>[]};
+ final _currentViews = <_ViewAndPort>[];
Function onChange;
- final Scope scope;
+ final Scope _scope;
- NgSwitch(this.scope) {
- cases['?'] = <_Case>[];
- }
+ NgSwitch(this._scope);
- addCase(String value, ViewPort anchor, BoundViewFactory viewFactory) {
- cases.putIfAbsent(value, () => <_Case>[]);
- cases[value].add(new _Case(anchor, viewFactory));
+ void addCase(String value, ViewPort anchor, BoundViewFactory viewFactory) {
+ _cases.putIfAbsent(value, () => <_Case>[]).add(new _Case(anchor, viewFactory));
}
- set value(val) {
- currentViews
- ..forEach((_ViewScopePair pair) {
- pair.port.remove(pair.view);
- pair.scope.destroy();
- })
- ..clear();
+ void set value(val) {
+ _currentViews..forEach((_ViewAndPort vp) => vp.remove())
+ ..clear();
val = '!$val';
- (cases.containsKey(val) ? cases[val] : cases['?'])
- .forEach((_Case caze) {
- Scope childScope = scope.createChild(new PrototypeMap(scope.context));
- var view = caze.viewFactory(childScope);
- caze.anchor.insert(view);
- currentViews.add(new _ViewScopePair(view, caze.anchor,
- childScope));
- });
- if (onChange != null) {
- onChange();
- }
+ var cases = _cases.containsKey(val) ? _cases[val] : _cases['?'];
+ cases.forEach((_Case c) {
+ // todo (vicb) -> create Child ?
+ var view = c.viewFactory(_scope);
+ c.port.insert(view);
+ _currentViews.add(new _ViewAndPort(view, c.port));
+ });
+
+ if (onChange != null) onChange();
}
}
-class _ViewScopePair {
- final View view;
- final ViewPort port;
- final Scope scope;
+class _ViewAndPort {
+ final View _view;
+ final ViewPort _port;
- _ViewScopePair(this.view, this.port, this.scope);
+ _ViewAndPort(this._view, this._port);
+
+ void remove() {
+ _port.remove(_view);
+ }
}
class _Case {
- final ViewPort anchor;
+ final ViewPort port;
final BoundViewFactory viewFactory;
- _Case(this.anchor, this.viewFactory);
+ _Case(this.port, this.viewFactory);
}
@Decorator(
@@ -114,23 +108,20 @@ class _Case {
children: Directive.TRANSCLUDE_CHILDREN,
map: const {'.': '@value'})
class NgSwitchWhen {
- final NgSwitch ngSwitch;
- final ViewPort port;
- final BoundViewFactory viewFactory;
- final Scope scope;
+ final NgSwitch _ngSwitch;
+ final ViewPort _port;
+ final BoundViewFactory _viewFactory;
- NgSwitchWhen(this.ngSwitch, this.port, this.viewFactory, this.scope);
+ NgSwitchWhen(this._ngSwitch, this._port, this._viewFactory);
- set value(String value) => ngSwitch.addCase('!$value', port, viewFactory);
+ void set value(String value) => _ngSwitch.addCase('!$value', _port, _viewFactory);
}
@Decorator(
children: Directive.TRANSCLUDE_CHILDREN,
selector: '[ng-switch-default]')
class NgSwitchDefault {
-
- NgSwitchDefault(NgSwitch ngSwitch, ViewPort port,
- BoundViewFactory viewFactory, Scope scope) {
+ NgSwitchDefault(NgSwitch ngSwitch, ViewPort port, BoundViewFactory viewFactory) {
ngSwitch.addCase('?', port, viewFactory);
}
}
diff --git a/lib/mock/test_bed.dart b/lib/mock/test_bed.dart
index e81f4d04e..fed2f92ea 100644
--- a/lib/mock/test_bed.dart
+++ b/lib/mock/test_bed.dart
@@ -27,10 +27,10 @@ class TestBed {
*
* - [String] then treat it as HTML
* - [Node] then treat it as the root node
- * - [List] then treat it as a collection of nods
+ * - [List] then treat it as a collection of nodes
*
* After the compilation the [rootElements] contains an array of compiled root nodes,
- * and [rootElement] contains the first element from the [rootElemets].
+ * and [rootElement] contains the first element from the [rootElements].
*
* An option [scope] parameter can be supplied to link it with non root scope.
*/
@@ -48,7 +48,7 @@ class TestBed {
} else {
throw 'Expecting: String, Node, or List got $html.';
}
- rootElement = rootElements.length > 0 && rootElements[0] is Element ? rootElements[0] : null;
+ rootElement = rootElements.isNotEmpty && rootElements[0] is Element ? rootElements[0] : null;
if (directives == null) {
directives = injector.get(DirectiveMap);
}
@@ -62,11 +62,7 @@ class TestBed {
List toNodeList(html) {
var div = new DivElement();
div.setInnerHtml(html, treeSanitizer: new NullTreeSanitizer());
- var nodes = [];
- for (var node in div.nodes) {
- nodes.add(node);
- }
- return nodes;
+ return new List.from(div.nodes);
}
/**
@@ -81,7 +77,7 @@ class TestBed {
/**
* Select an [OPTION] in a [SELECT] with a given name and trigger the
- * appropriate DOM event. Used when testing [SELECT] controlls in forms.
+ * appropriate DOM event. Used when testing [SELECT] controls in forms.
*/
selectOption(element, text) {
element.querySelectorAll('option').forEach((o) => o.selected = o.text == text);
@@ -98,5 +94,5 @@ class TestBed {
throw 'Probe not found.';
}
- getScope(Node node) => getProbe(node).scope;
+ Scope getScope(Node node) => getProbe(node).scope;
}
diff --git a/lib/routing/ng_view.dart b/lib/routing/ng_view.dart
index 248927ff7..6a8f63dc4 100644
--- a/lib/routing/ng_view.dart
+++ b/lib/routing/ng_view.dart
@@ -122,9 +122,8 @@ class NgView implements DetachAware, RouteProvider {
viewCache.fromUrl(viewDef.template, newDirectives);
viewFuture.then((viewFactory) {
_cleanUp();
- _scope = scope.createChild(new PrototypeMap(scope.context));
_view = viewFactory(
- viewInjector.createChild([new Module()..bind(Scope, toValue: _scope)]));
+ viewInjector.createChild([new Module()..bind(Scope, toValue: scope)]));
_view.nodes.forEach((elm) => element.append(elm));
});
}
@@ -133,10 +132,8 @@ class NgView implements DetachAware, RouteProvider {
if (_view == null) return;
_view.nodes.forEach((node) => node.remove());
- _scope.destroy();
_view = null;
- _scope = null;
}
Route get route => _viewRoute;
@@ -154,7 +151,7 @@ class NgView implements DetachAware, RouteProvider {
/**
- * Class that can be injected to retrieve information about the current route.
+ * Inject a [RouteProvider] to retrieve information about the current route.
* For example:
*
* @Component(/* ... */)
@@ -173,7 +170,7 @@ class NgView implements DetachAware, RouteProvider {
* });
* }
*
- * detach() {
+ * void detach() {
* // The route handle must be discarded.
* route.discard();
* }
@@ -183,8 +180,8 @@ class NgView implements DetachAware, RouteProvider {
* }
* }
*
- * If user component is used outside of ng-view directive then
- * injected [RouteProvider] will be null.
+ * If user component is used outside of ng-view directive then the injected [RouteProvider] will be
+ * null.
*/
abstract class RouteProvider {
diff --git a/lib/transformer.dart b/lib/transformer.dart
index 0d8668203..5f2f888bb 100644
--- a/lib/transformer.dart
+++ b/lib/transformer.dart
@@ -36,7 +36,6 @@ TransformOptions _parseSettings(Map args) {
var annotations = [
'angular.core.annotation_src.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/test/core/annotation_src_spec.dart b/test/core/annotation_src_spec.dart
index 2bfeafce3..ddf508173 100644
--- a/test/core/annotation_src_spec.dart
+++ b/test/core/annotation_src_spec.dart
@@ -39,7 +39,6 @@ void main() => describe('annotations', () {
cssUrl: [''],
applyAuthorStyles: true,
resetStyleInheritance: true,
- publishAs: '',
module: (){},
map: {},
selector: '',
@@ -76,25 +75,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: (){},
- 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/core_directive_spec.dart b/test/core/core_directive_spec.dart
index ab4cd3149..c082e868b 100644
--- a/test/core/core_directive_spec.dart
+++ b/test/core/core_directive_spec.dart
@@ -23,7 +23,6 @@ void main() {
expect(annotation.template).toEqual('template');
expect(annotation.templateUrl).toEqual('templateUrl');
expect(annotation.cssUrls).toEqual(['cssUrls']);
- expect(annotation.publishAs).toEqual('ctrl');
expect(annotation.map).toEqual({
'foo': '=>foo',
'attr': '@attr',
@@ -104,15 +103,14 @@ class NullParser implements Parser {
template: 'template',
templateUrl: 'templateUrl',
cssUrl: const ['cssUrls'],
- publishAs: 'ctrl',
module: AnnotatedIoComponent.module,
visibility: Directive.LOCAL_VISIBILITY,
exportExpressions: const ['exportExpressions'],
- map: const {
- 'foo': '=>foo'
- })
+ map: const {'foo': '=>foo'})
class AnnotatedIoComponent {
- static module() => new Module()..bind(String, toFactory: (i) => i.get(AnnotatedIoComponent),
+ static module() => new Module()..bind(
+ String,
+ toFactory: (i) => i.get(AnnotatedIoComponent),
visibility: Directive.LOCAL_VISIBILITY);
AnnotatedIoComponent(Scope scope) {
diff --git a/test/core_dom/compiler_spec.dart b/test/core_dom/compiler_spec.dart
index 750ed46c5..460a28670 100644
--- a/test/core_dom/compiler_spec.dart
+++ b/test/core_dom/compiler_spec.dart
@@ -758,18 +758,12 @@ void main() {
}
-@Controller(
- selector: '[my-parent-controller]',
- publishAs: 'my_parent')
+@Controller(selector: '[my-parent-controller]')
class MyParentController {
- data() {
- return "my data";
- }
+ String data() => "my data";
}
-@Controller(
- selector: '[my-child-controller]',
- publishAs: 'my_child')
+@Controller(selector: '[my-child-controller]')
class MyChildController {}
@Component(
@@ -882,8 +876,7 @@ class SimpleComponent {
@Component(
selector: 'shadowy',
template: r'With shadow DOM',
- useShadowDom: true
-)
+ useShadowDom: true)
class ShadowyComponent {
ShadowyComponent(Logger log) {
log('shadowy');
@@ -893,8 +886,7 @@ class ShadowyComponent {
@Component(
selector: 'shadowless',
template: r'Without shadow DOM',
- useShadowDom: false
-)
+ useShadowDom: false)
class ShadowlessComponent {
ShadowlessComponent(Logger log) {
log('shadowless');
@@ -903,13 +895,13 @@ class ShadowlessComponent {
@Component(
selector: 'sometimes',
- template: r'
\ No newline at end of file
diff --git a/test/tools/html_extractor_spec.dart b/test/tools/html_extractor_spec.dart
index 976e19442..7eefd6f6c 100644
--- a/test/tools/html_extractor_spec.dart
+++ b/test/tools/html_extractor_spec.dart
@@ -13,40 +13,40 @@ void main() {
it('should extract text mustache expressions', () {
var ioService = new MockIoService({
'foo.html': r'''
-
foo {{ctrl.bar}} baz {{aux}}
+
foo {{bar}} baz {{aux}}
'''
});
var extractor = new HtmlExpressionExtractor([]);
extractor.crawl('/', ioService);
expect(extractor.expressions.toList()..sort(),
- equals(['aux', 'ctrl.bar']));
+ equals(['aux', 'bar']));
});
it('should extract attribute mustache expressions', () {
var ioService = new MockIoService({
'foo.html': r'''
-
+
'''
});
var extractor = new HtmlExpressionExtractor([]);
extractor.crawl('/', ioService);
expect(extractor.expressions.toList()..sort(),
- equals(['aux', 'ctrl.bar']));
+ equals(['aux', 'bar']));
});
it('should extract ng-repeat expressions', () {
var ioService = new MockIoService({
'foo.html': r'''
-
+
'''
});
var extractor = new HtmlExpressionExtractor([]);
extractor.crawl('/', ioService);
expect(extractor.expressions.toList()..sort(),
- equals(['ctrl.bar']));
+ equals(['bar']));
});
it('should extract expressions provided in the directive info', () {
@@ -62,9 +62,7 @@ void main() {
it('should extract expressions from expression attributes', () {
var ioService = new MockIoService({
- 'foo.html': r'''
-
- '''
+ 'foo.html': r''
});
var extractor = new HtmlExpressionExtractor([
@@ -72,14 +70,12 @@ void main() {
]);
extractor.crawl('/', ioService);
expect(extractor.expressions.toList()..sort(),
- equals(['ctrl.baz']));
+ equals(['baz']));
});
it('should ignore ng-repeat while extracting attribute expressions', () {
var ioService = new MockIoService({
- 'foo.html': r'''
-
- '''
+ 'foo.html': r''
});
var extractor = new HtmlExpressionExtractor([
@@ -88,7 +84,7 @@ void main() {
extractor.crawl('/', ioService);
// Basically we don't want to extract "foo in ctrl.bar".
expect(extractor.expressions.toList()..sort(),
- equals(['ctrl.bar']));
+ equals(['bar']));
});
});
}
diff --git a/test/tools/source_metadata_extractor_spec.dart b/test/tools/source_metadata_extractor_spec.dart
index 4a4c6c5e8..2f63c84fb 100644
--- a/test/tools/source_metadata_extractor_spec.dart
+++ b/test/tools/source_metadata_extractor_spec.dart
@@ -105,11 +105,11 @@ void main() {
var info = extractDirectiveInfo([
new DirectiveMetadata('MyFooAttrDirective', DIRECTIVE, '[blah][foo]', {
'.': '=>fooExpr'
- }, null, ['ctrl.baz'])
+ }, null, ['baz'])
]);
expect(flattenList(info, (DirectiveInfo i) => i.expressions),
- equals(['fooExpr', 'ctrl.baz']));
+ equals(['fooExpr', 'baz']));
});
});