Index: lib/templating.dart |
diff --git a/lib/templating.dart b/lib/templating.dart |
index c8dfc10c791aad95ae0f91d36566ead4ae753244..28646deafa7c44e9184bb00c4e819dcac533bf5d 100644 |
--- a/lib/templating.dart |
+++ b/lib/templating.dart |
@@ -6,9 +6,11 @@ |
library templating; |
import 'dart:async'; |
+import 'dart:collection'; |
import 'dart:html'; |
import 'dart:uri'; |
import 'package:web_ui/safe_html.dart'; |
+import 'package:web_ui/observe.dart'; |
import 'package:web_ui/watcher.dart'; |
/** |
@@ -136,16 +138,16 @@ void updateCssClass(Element elem, bool addClasses, classes) { |
* bindCssClasses(e, () => class1); |
* bindCssClasses(e, () => class2); |
*/ |
-WatcherDisposer bindCssClasses(Element elem, dynamic exp()) { |
- return watchAndInvoke(exp, (e) { |
+ChangeUnobserver bindCssClasses(Element elem, dynamic exp()) { |
+ return watchAndInvoke(_observeList(exp), (e) { |
updateCssClass(elem, false, e.oldValue); |
updateCssClass(elem, true, e.newValue); |
}, 'css-class-bind'); |
} |
/** Bind the result of [exp] to the style attribute in [elem]. */ |
-WatcherDisposer bindStyle(Element elem, Map<String, String> exp()) { |
- return watchAndInvoke(exp, (e) { |
+ChangeUnobserver bindStyle(Element elem, Map<String, String> exp()) { |
+ return watchAndInvoke(_observeMap(exp), (e) { |
if (e.oldValue is Map<String, String>) { |
var props = e.newValue; |
if (props is! Map<String, String>) props = const {}; |
@@ -233,8 +235,8 @@ class Listener extends TemplateItem { |
/** Represents a generic data binding and a corresponding action. */ |
class Binding extends TemplateItem { |
final exp; |
- final ValueWatcher action; |
- WatcherDisposer stopper; |
+ final ChangeObserver action; |
+ ChangeUnobserver stopper; |
Binding(this.exp, this.action); |
@@ -253,7 +255,7 @@ class Binding extends TemplateItem { |
class StyleAttrBinding extends TemplateItem { |
final exp; |
final Element elem; |
- WatcherDisposer stopper; |
+ ChangeUnobserver stopper; |
StyleAttrBinding(this.elem, this.exp); |
@@ -272,7 +274,7 @@ class StyleAttrBinding extends TemplateItem { |
class ClassAttrBinding extends TemplateItem { |
final Element elem; |
final exp; |
- WatcherDisposer stopper; |
+ ChangeUnobserver stopper; |
ClassAttrBinding(this.elem, this.exp); |
@@ -307,7 +309,7 @@ class DomPropertyBinding extends TemplateItem { |
*/ |
final bool isUrl; |
- WatcherDisposer stopper; |
+ ChangeUnobserver stopper; |
DomPropertyBinding(this.getter, this.setter, this.isUrl); |
@@ -371,7 +373,7 @@ class Template extends TemplateItem { |
} |
/** Run [action] when [exp] changes (while this template is visible). */ |
- void bind(exp, ValueWatcher action) { |
+ void bind(exp, ChangeObserver action) { |
children.add(new Binding(exp, action)); |
} |
@@ -476,7 +478,7 @@ abstract class PlaceholderTemplate extends Template { |
/** Expression watch by this template (condition or loop expression). */ |
final exp; |
- WatcherDisposer stopper; |
+ ChangeUnobserver stopper; |
PlaceholderTemplate(Node reference, this.exp) |
: super(reference); |
@@ -544,6 +546,31 @@ class ConditionalTemplate extends PlaceholderTemplate { |
} |
} |
+_observeList(exp) { |
+ if (!useObservers) return exp; |
+ |
+ // TODO(jmesserly): implement detailed change records in Observable* types, |
+ // so we can observe the list itself and react to all changes. |
+ // For now we call .toList which causes us to depend on all items. This |
+ // also works for Iterables. |
+ return () { |
+ var x = exp(); |
+ return x is Iterable ? x.toList() : x; |
+ }; |
+} |
+ |
+_observeMap(exp) { |
+ if (!useObservers) return exp; |
+ |
+ // TODO(jmesserly): this has similar issues as _observeList. Ideally |
+ // observers can observe all changes to the resulting map, so we don't need a |
+ // copy here. |
+ return () { |
+ var x = exp(); |
+ return x is Map ? new LinkedHashMap.from(x) : x; |
+ }; |
+} |
+ |
/** Function to set up the contents of a loop template. */ |
typedef void LoopIterationSetup(loopVariable, Template template); |
@@ -554,7 +581,7 @@ class LoopTemplate extends PlaceholderTemplate { |
LoopTemplate(Node reference, exp, this.iterSetup) : super(reference, exp); |
void insert() { |
- stopper = watchAndInvoke(exp, (e) { |
+ stopper = watchAndInvoke(_observeList(exp), (e) { |
super.remove(); |
for (var x in e.newValue) { |
iterSetup(x, this); |
@@ -578,7 +605,7 @@ class LoopTemplate extends PlaceholderTemplate { |
class LoopTemplateInAttribute extends Template { |
final LoopIterationSetup iterSetup; |
final exp; |
- WatcherDisposer stopper; |
+ ChangeUnobserver stopper; |
LoopTemplateInAttribute(Node node, this.exp, this.iterSetup) : super(node); |
@@ -586,7 +613,7 @@ class LoopTemplateInAttribute extends Template { |
void create() {} |
void insert() { |
- stopper = watchAndInvoke(exp, (e) { |
+ stopper = watchAndInvoke(_observeList(exp), (e) { |
_removeInternal(); |
for (var x in e.newValue) { |
iterSetup(x, this); |