OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library template_binding.src.node_binding; | |
6 | |
7 import 'dart:async' show StreamSubscription; | |
8 import 'dart:html' show Node; | |
9 import 'package:observe/observe.dart' show PathObserver, CompoundPathObserver; | |
10 | |
11 /** Test only method. Not re-exported. */ | |
12 getObserverForTest(NodeBinding binding) => binding._observer; | |
13 | |
14 /** | |
15 * A data binding on a [Node]. | |
16 * See [NodeBindExtension.bindings] and [NodeBindExtension.bind]. | |
17 */ | |
18 abstract class NodeBinding { | |
19 Node _node; | |
20 var _model; | |
21 | |
22 // TODO(jmesserly): need common interface for PathObserver, | |
23 // CompoundPathObserver. | |
24 var _observer; | |
25 StreamSubscription _pathSub; | |
26 | |
27 /** The property of [node] which will be data bound. */ | |
28 final String property; | |
29 | |
30 /** The property of [node] which will be data bound. */ | |
31 final String path; | |
32 | |
33 /** The node that has [property] which will be data bound. */ | |
34 Node get node => _node; | |
35 | |
36 /** The bound data model. */ | |
37 get model => _model; | |
38 | |
39 /** True if this binding has been [closed]. */ | |
40 bool get closed => _node == null; | |
41 | |
42 /** The value at the [path] on [model]. */ | |
43 get value => _observer.value; | |
44 | |
45 set value(newValue) { | |
46 _observer.value = newValue; | |
47 } | |
48 | |
49 NodeBinding(this._node, this.property, this._model, [String path]) | |
50 : path = path != null ? path : '' { | |
51 | |
52 // Fast path if we're observing "value" | |
53 if ((model is PathObserver || model is CompoundPathObserver) && | |
54 path == 'value') { | |
55 | |
56 _observer = model; | |
57 } else { | |
58 // Create the path observer | |
59 _observer = new PathObserver(model, this.path); | |
60 } | |
61 | |
62 _pathSub = _observer.changes.listen((r) => valueChanged(value)); | |
63 valueChanged(value); | |
64 } | |
65 | |
66 /** Called when [value] changes to update the [node]. */ | |
67 // TODO(jmesserly): the impl in template_binding uses reflection to set the | |
68 // property, but that isn't used except for specific known fields like | |
69 // "textContent", so I'm overridding this in the subclasses instead. | |
70 void valueChanged(newValue); | |
71 | |
72 /** Called to sanitize the value before it is assigned into the property. */ | |
73 sanitizeBoundValue(value) => value == null ? '' : '$value'; | |
74 | |
75 /** | |
76 * Called by [NodeBindExtension.unbind] to close this binding and unobserve | |
77 * the [path]. | |
78 * | |
79 * This can be overridden in subclasses, but they must call `super.close()` | |
80 * to free associated resources. They must also check [closed] and return | |
81 * immediately if already closed. | |
82 */ | |
83 void close() { | |
84 if (closed) return; | |
85 | |
86 if (_pathSub != null) _pathSub.cancel(); | |
87 _pathSub = null; | |
88 _observer = null; | |
89 _node = null; | |
90 _model = null; | |
91 } | |
92 } | |
OLD | NEW |