Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(204)

Side by Side Diff: pkg/template_binding/lib/src/node.dart

Issue 34453003: Rename mdv -> template_binding, remove experiemntal apis from dart:html (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 part of template_binding;
6
7 /** Extensions to the [Node] API. */
8 class NodeBindExtension {
9 final Node _node;
10 Map<String, NodeBinding> _bindings;
11
12 NodeBindExtension._(this._node);
13
14 /**
15 * Creates a binding to the attribute [name] to the [path] of the [model].
16 *
17 * This can be overridden by custom elements to provide the binding used in
18 * [bind]. This will only create the binding; it will not add
19 * it to [bindings].
20 *
21 * You should not need to call this directly except from [bind].
22 */
23 NodeBinding createBinding(String name, model, String path) => null;
24
25 /**
26 * Binds the attribute [name] to the [path] of the [model].
27 * Path is a String of accessors such as `foo.bar.baz`.
28 * Returns the `NodeBinding` instance.
29 */
30 NodeBinding bind(String name, model, String path) {
31 var binding = bindings[name];
32 if (binding != null) binding.close();
33
34 // Note: dispatch through the node so it can override this.
35 binding = nodeBind(_node).createBinding(name, model, path);
36
37 bindings[name] = binding;
38 if (binding == null) {
39 window.console.error('Unhandled binding to Node: '
40 '$this $name $model $path');
41 }
42 return binding;
43 }
44
45 /** Unbinds the attribute [name]. */
46 void unbind(String name) {
47 if (_bindings == null) return;
48 var binding = bindings.remove(name);
49 if (binding != null) binding.close();
50 }
51
52 /** Unbinds all bound attributes. */
53 void unbindAll() {
54 if (_bindings == null) return;
55 for (var binding in bindings.values) {
56 if (binding != null) binding.close();
57 }
58 _bindings = null;
59 }
60
61 // TODO(jmesserly): we should return a read-only wrapper here.
62 /** Gets the data bindings that are associated with this node. */
63 Map<String, NodeBinding> get bindings {
64 if (_bindings == null) _bindings = new LinkedHashMap<String, NodeBinding>();
65 return _bindings;
66 }
67
68 TemplateInstance _templateInstance;
69
70 /** Gets the template instance that instantiated this node, if any. */
71 TemplateInstance get templateInstance =>
72 _templateInstance != null ? _templateInstance :
73 (_node.parent != null ? _node.parent.templateInstance : null);
74 }
75
76
77 /** Information about the instantiated template. */
78 class TemplateInstance {
79 // TODO(rafaelw): firstNode & lastNode should be read-synchronous
80 // in cases where script has modified the template instance boundary.
81
82 /** The first node of this template instantiation. */
83 final Node firstNode;
84
85 /**
86 * The last node of this template instantiation.
87 * This could be identical to [firstNode] if the template only expanded to a
88 * single node.
89 */
90 final Node lastNode;
91
92 /** The model used to instantiate the template. */
93 final model;
94
95 TemplateInstance(this.firstNode, this.lastNode, this.model);
96 }
97
98
99 /**
100 * Template Bindings native features enables a wide-range of use cases,
101 * but (by design) don't attempt to implement a wide array of specialized
102 * behaviors.
103 *
104 * Enabling these features is a matter of implementing and registering a
105 * BindingDelegate. A binding delegate is an object which contains one or more
106 * delegation functions which implement specialized behavior. This object is
107 * registered via [TemplateBindExtension.bindingDelegate]:
108 *
109 * HTML:
110 * <template bind>
111 * {{ What!Ever('crazy')->thing^^^I+Want(data) }}
112 * </template>
113 *
114 * Dart:
115 * class MySyntax extends BindingDelegate {
116 * getBinding(model, path, name, node) {
117 * // The magic happens here!
118 * }
119 * }
120 * ...
121 * templateBind(query('template'))
122 * ..bindingDelegate = new MySyntax()
123 * ..model = new MyModel();
124 *
125 * See <https://github.com/polymer-project/mdv/blob/master/docs/syntax.md> for
126 * more information about Custom Syntax.
127 */
128 abstract class BindingDelegate {
129 /**
130 * This syntax method allows for a custom interpretation of the contents of
131 * mustaches (`{{` ... `}}`).
132 *
133 * When a template is inserting an instance, it will invoke this method for
134 * each mustache which is encountered. The function is invoked with four
135 * arguments:
136 *
137 * - [model]: The data context for which this instance is being created.
138 * - [path]: The text contents (trimmed of outer whitespace) of the mustache.
139 * - [name]: The context in which the mustache occurs. Within element
140 * attributes, this will be the name of the attribute. Within text,
141 * this will be 'text'.
142 * - [node]: A reference to the node to which this binding will be created.
143 *
144 * If the method wishes to handle binding, it is required to return an object
145 * which has at least a `value` property that can be observed. If it does,
146 * then MDV will call [NodeBindExtension.bind] on the node:
147 *
148 * nodeBind(node).bind(name, retval, 'value');
149 *
150 * If the 'getBinding' does not wish to override the binding, it should return
151 * null.
152 */
153 // TODO(jmesserly): I had to remove type annotations from "name" and "node"
154 // Normally they are String and Node respectively. But sometimes it will pass
155 // (int name, CompoundBinding node). That seems very confusing; we may want
156 // to change this API.
157 getBinding(model, String path, name, node) => null;
158
159 /**
160 * This syntax method allows a syntax to provide an alterate model than the
161 * one the template would otherwise use when producing an instance.
162 *
163 * When a template is about to create an instance, it will invoke this method
164 * The function is invoked with two arguments:
165 *
166 * - [template]: The template element which is about to create and insert an
167 * instance.
168 * - [model]: The data context for which this instance is being created.
169 *
170 * The template element will always use the return value of `getInstanceModel`
171 * as the model for the new instance. If the syntax does not wish to override
172 * the value, it should simply return the `model` value it was passed.
173 */
174 getInstanceModel(Element template, model) => model;
175 }
176
177 /**
178 * A data binding on a [Node].
179 * See [NodeBindExtension.bindings] and [NodeBindExtension.bind].
180 */
181 abstract class NodeBinding {
182 Node _node;
183 var _model;
184 PathObserver _observer;
185 StreamSubscription _pathSub;
186
187 /** The property of [node] which will be data bound. */
188 final String property;
189
190 /** The property of [node] which will be data bound. */
191 final String path;
192
193 /** The node that has [property] which will be data bound. */
194 Node get node => _node;
195
196 /**
197 * The bound data model.
198 */
199 get model => _model;
200
201 /** True if this binding has been [closed]. */
202 bool get closed => _observer == null;
203
204 /** The value at the [path] on [model]. */
205 get value => _observer.value;
206
207 set value(newValue) {
208 _observer.value = newValue;
209 }
210
211 NodeBinding(this._node, this.property, this._model, this.path) {
212 // Create the path observer
213 _observer = new PathObserver(model, path);
214 _observePath();
215 }
216
217 void _observePath() {
218 _pathSub = _observer.bindSync(boundValueChanged);
219 }
220
221 /** Called when [value] changes to update the [node]. */
222 // TODO(jmesserly): the impl in MDV uses mirrors to set the property,
223 // but that isn't used except for specific known fields like "textContent",
224 // so I'm overridding this in the subclasses instead.
225 void boundValueChanged(newValue);
226
227 /** Called to sanitize the value before it is assigned into the property. */
228 sanitizeBoundValue(value) => value == null ? '' : '$value';
229
230 /**
231 * Called by [NodeBindExtension.unbind] to close this binding and unobserve
232 * the [path].
233 *
234 * This can be overridden in subclasses, but they must call `super.close()`
235 * to free associated resources. They must also check [closed] and return
236 * immediately if already closed.
237 */
238 void close() {
239 if (closed) return;
240
241 if (_pathSub != null) _pathSub.cancel();
242 _pathSub = null;
243 _observer = null;
244 _node = null;
245 _model = null;
246 }
247 }
OLDNEW
« no previous file with comments | « pkg/template_binding/lib/src/list_diff.dart ('k') | pkg/template_binding/lib/src/select_element.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698