Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of mdv; | 5 part of mdv; |
| 6 | 6 |
| 7 /** Extensions to the [Node] API. */ | 7 /** Extensions to the [Node] API. */ |
| 8 class _NodeExtension { | 8 class _NodeExtension { |
| 9 final Node node; | 9 final Node node; |
| 10 Map<String, NodeBinding> _bindings; | |
| 10 | 11 |
| 11 _NodeExtension(this.node); | 12 _NodeExtension(this.node); |
| 12 | 13 |
| 14 NodeBinding createBinding(String name, model, String path) => null; | |
| 15 | |
| 13 /** | 16 /** |
| 14 * Binds the attribute [name] to the [path] of the [model]. | 17 * Binds the attribute [name] to the [path] of the [model]. |
| 15 * Path is a String of accessors such as `foo.bar.baz`. | 18 * Path is a String of accessors such as `foo.bar.baz`. |
| 16 */ | 19 */ |
| 17 void bind(String name, model, String path) { | 20 NodeBinding bind(String name, model, String path) { |
| 18 window.console.error('Unhandled binding to Node: ' | 21 var binding = bindings[name]; |
| 19 '$this $name $model $path'); | 22 if (binding != null) binding.close(); |
| 23 | |
| 24 binding = createBinding(name, model, path); | |
| 25 bindings[name] = binding; | |
| 26 if (binding == null) { | |
| 27 window.console.error('Unhandled binding to Node: ' | |
| 28 '$this $name $model $path'); | |
| 29 } | |
| 30 return binding; | |
| 20 } | 31 } |
| 21 | 32 |
| 22 /** Unbinds the attribute [name]. */ | 33 /** Unbinds the attribute [name]. */ |
| 23 void unbind(String name) {} | 34 void unbind(String name) { |
| 35 if (_bindings == null) return; | |
| 36 var binding = bindings.remove(name); | |
| 37 if (binding != null) binding.close(); | |
| 38 } | |
| 24 | 39 |
| 25 /** Unbinds all bound attributes. */ | 40 /** Unbinds all bound attributes. */ |
| 26 void unbindAll() {} | 41 void unbindAll() { |
| 42 if (_bindings == null) return; | |
| 43 for (var binding in bindings.values) { | |
| 44 if (binding != null) binding.close(); | |
| 45 } | |
| 46 _bindings = null; | |
| 47 } | |
| 48 | |
| 49 // TODO(jmesserly): we should return a read-only wrapper here. | |
| 50 Map<String, NodeBinding> get bindings { | |
| 51 if (_bindings == null) _bindings = new LinkedHashMap<String, NodeBinding>(); | |
| 52 return _bindings; | |
| 53 } | |
| 27 | 54 |
| 28 TemplateInstance _templateInstance; | 55 TemplateInstance _templateInstance; |
| 29 | 56 |
| 30 /** Gets the template instance that instantiated this node, if any. */ | 57 /** Gets the template instance that instantiated this node, if any. */ |
| 31 TemplateInstance get templateInstance => | 58 TemplateInstance get templateInstance => |
| 32 _templateInstance != null ? _templateInstance : | 59 _templateInstance != null ? _templateInstance : |
| 33 (node.parent != null ? node.parent.templateInstance : null); | 60 (node.parent != null ? node.parent.templateInstance : null); |
| 34 } | 61 } |
| 62 | |
| 63 /** A data binding on a [Node]. See [Node.bindings] and [Node.bind]. */ | |
| 64 abstract class NodeBinding { | |
| 65 Node _node; | |
| 66 var _model; | |
| 67 PathObserver _observer; | |
| 68 StreamSubscription _pathSub; | |
| 69 | |
| 70 /** The property of [node] which will be data bound. */ | |
| 71 final String property; | |
| 72 | |
| 73 /** The property of [node] which will be data bound. */ | |
| 74 final String path; | |
| 75 | |
| 76 /** The node that has [property] which will be data bound. */ | |
| 77 Node get node => _node; | |
| 78 | |
| 79 /** | |
| 80 * The bound data model. | |
|
Siggi Cherem (dart-lang)
2013/07/25 16:24:12
silly nit: this fits in 1 line (/** ... */)
| |
| 81 */ | |
| 82 get model => _model; | |
| 83 | |
| 84 /** True if this binding has been [closed]. */ | |
| 85 bool get closed => _observer == null; | |
| 86 | |
| 87 /** The value at the [path] on [model]. */ | |
| 88 get value => _observer.value; | |
| 89 | |
| 90 set value(newValue) { | |
| 91 _observer.value = newValue; | |
| 92 } | |
| 93 | |
| 94 NodeBinding(this._node, this.property, this._model, this.path) { | |
| 95 // Create the path observer | |
| 96 _observer = new PathObserver(model, path); | |
| 97 _observePath(); | |
| 98 } | |
| 99 | |
| 100 void _observePath() { | |
| 101 _pathSub = _observer.bindSync(boundValueChanged); | |
| 102 } | |
| 103 | |
| 104 /** Called when [value] changes to update the [node]. */ | |
| 105 // TODO(jmesserly): the impl in MDV uses mirrors to set the property, | |
| 106 // but that isn't used except for specific known fields like "textContent", | |
| 107 // so I'm overridding this in the subclasses instead. | |
| 108 void boundValueChanged(newValue); | |
| 109 | |
| 110 /** Called to sanitize the value before it is assigned into the property. */ | |
| 111 sanitizeBoundValue(value) => value == null ? '' : '$value'; | |
| 112 | |
| 113 /** | |
| 114 * Called by [Node.unbind] to close this binding and unobserve the [path]. | |
| 115 * | |
| 116 * This can be overridden in subclasses, but they must call `super.close()` | |
| 117 * to free associated resources. They must also check [closed] and return | |
| 118 * immediately if already closed. | |
| 119 */ | |
| 120 void close() { | |
| 121 if (closed) return; | |
| 122 | |
| 123 if (_pathSub != null) _pathSub.cancel(); | |
| 124 _pathSub = null; | |
| 125 _observer = null; | |
| 126 _node = null; | |
| 127 _model = null; | |
| 128 } | |
| 129 } | |
| OLD | NEW |