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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/template_binding/lib/src/node.dart
diff --git a/pkg/template_binding/lib/src/node.dart b/pkg/template_binding/lib/src/node.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a1bc29c99422d7df5b29d8827cdc48a4020f5832
--- /dev/null
+++ b/pkg/template_binding/lib/src/node.dart
@@ -0,0 +1,247 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of template_binding;
+
+/** Extensions to the [Node] API. */
+class NodeBindExtension {
+ final Node _node;
+ Map<String, NodeBinding> _bindings;
+
+ NodeBindExtension._(this._node);
+
+ /**
+ * Creates a binding to the attribute [name] to the [path] of the [model].
+ *
+ * This can be overridden by custom elements to provide the binding used in
+ * [bind]. This will only create the binding; it will not add
+ * it to [bindings].
+ *
+ * You should not need to call this directly except from [bind].
+ */
+ NodeBinding createBinding(String name, model, String path) => null;
+
+ /**
+ * Binds the attribute [name] to the [path] of the [model].
+ * Path is a String of accessors such as `foo.bar.baz`.
+ * Returns the `NodeBinding` instance.
+ */
+ NodeBinding bind(String name, model, String path) {
+ var binding = bindings[name];
+ if (binding != null) binding.close();
+
+ // Note: dispatch through the node so it can override this.
+ binding = nodeBind(_node).createBinding(name, model, path);
+
+ bindings[name] = binding;
+ if (binding == null) {
+ window.console.error('Unhandled binding to Node: '
+ '$this $name $model $path');
+ }
+ return binding;
+ }
+
+ /** Unbinds the attribute [name]. */
+ void unbind(String name) {
+ if (_bindings == null) return;
+ var binding = bindings.remove(name);
+ if (binding != null) binding.close();
+ }
+
+ /** Unbinds all bound attributes. */
+ void unbindAll() {
+ if (_bindings == null) return;
+ for (var binding in bindings.values) {
+ if (binding != null) binding.close();
+ }
+ _bindings = null;
+ }
+
+ // TODO(jmesserly): we should return a read-only wrapper here.
+ /** Gets the data bindings that are associated with this node. */
+ Map<String, NodeBinding> get bindings {
+ if (_bindings == null) _bindings = new LinkedHashMap<String, NodeBinding>();
+ return _bindings;
+ }
+
+ TemplateInstance _templateInstance;
+
+ /** Gets the template instance that instantiated this node, if any. */
+ TemplateInstance get templateInstance =>
+ _templateInstance != null ? _templateInstance :
+ (_node.parent != null ? _node.parent.templateInstance : null);
+}
+
+
+/** Information about the instantiated template. */
+class TemplateInstance {
+ // TODO(rafaelw): firstNode & lastNode should be read-synchronous
+ // in cases where script has modified the template instance boundary.
+
+ /** The first node of this template instantiation. */
+ final Node firstNode;
+
+ /**
+ * The last node of this template instantiation.
+ * This could be identical to [firstNode] if the template only expanded to a
+ * single node.
+ */
+ final Node lastNode;
+
+ /** The model used to instantiate the template. */
+ final model;
+
+ TemplateInstance(this.firstNode, this.lastNode, this.model);
+}
+
+
+/**
+ * Template Bindings native features enables a wide-range of use cases,
+ * but (by design) don't attempt to implement a wide array of specialized
+ * behaviors.
+ *
+ * Enabling these features is a matter of implementing and registering a
+ * BindingDelegate. A binding delegate is an object which contains one or more
+ * delegation functions which implement specialized behavior. This object is
+ * registered via [TemplateBindExtension.bindingDelegate]:
+ *
+ * HTML:
+ * <template bind>
+ * {{ What!Ever('crazy')->thing^^^I+Want(data) }}
+ * </template>
+ *
+ * Dart:
+ * class MySyntax extends BindingDelegate {
+ * getBinding(model, path, name, node) {
+ * // The magic happens here!
+ * }
+ * }
+ * ...
+ * templateBind(query('template'))
+ * ..bindingDelegate = new MySyntax()
+ * ..model = new MyModel();
+ *
+ * See <https://github.com/polymer-project/mdv/blob/master/docs/syntax.md> for
+ * more information about Custom Syntax.
+ */
+abstract class BindingDelegate {
+ /**
+ * This syntax method allows for a custom interpretation of the contents of
+ * mustaches (`{{` ... `}}`).
+ *
+ * When a template is inserting an instance, it will invoke this method for
+ * each mustache which is encountered. The function is invoked with four
+ * arguments:
+ *
+ * - [model]: The data context for which this instance is being created.
+ * - [path]: The text contents (trimmed of outer whitespace) of the mustache.
+ * - [name]: The context in which the mustache occurs. Within element
+ * attributes, this will be the name of the attribute. Within text,
+ * this will be 'text'.
+ * - [node]: A reference to the node to which this binding will be created.
+ *
+ * If the method wishes to handle binding, it is required to return an object
+ * which has at least a `value` property that can be observed. If it does,
+ * then MDV will call [NodeBindExtension.bind] on the node:
+ *
+ * nodeBind(node).bind(name, retval, 'value');
+ *
+ * If the 'getBinding' does not wish to override the binding, it should return
+ * null.
+ */
+ // TODO(jmesserly): I had to remove type annotations from "name" and "node"
+ // Normally they are String and Node respectively. But sometimes it will pass
+ // (int name, CompoundBinding node). That seems very confusing; we may want
+ // to change this API.
+ getBinding(model, String path, name, node) => null;
+
+ /**
+ * This syntax method allows a syntax to provide an alterate model than the
+ * one the template would otherwise use when producing an instance.
+ *
+ * When a template is about to create an instance, it will invoke this method
+ * The function is invoked with two arguments:
+ *
+ * - [template]: The template element which is about to create and insert an
+ * instance.
+ * - [model]: The data context for which this instance is being created.
+ *
+ * The template element will always use the return value of `getInstanceModel`
+ * as the model for the new instance. If the syntax does not wish to override
+ * the value, it should simply return the `model` value it was passed.
+ */
+ getInstanceModel(Element template, model) => model;
+}
+
+/**
+ * A data binding on a [Node].
+ * See [NodeBindExtension.bindings] and [NodeBindExtension.bind].
+ */
+abstract class NodeBinding {
+ Node _node;
+ var _model;
+ PathObserver _observer;
+ StreamSubscription _pathSub;
+
+ /** The property of [node] which will be data bound. */
+ final String property;
+
+ /** The property of [node] which will be data bound. */
+ final String path;
+
+ /** The node that has [property] which will be data bound. */
+ Node get node => _node;
+
+ /**
+ * The bound data model.
+ */
+ get model => _model;
+
+ /** True if this binding has been [closed]. */
+ bool get closed => _observer == null;
+
+ /** The value at the [path] on [model]. */
+ get value => _observer.value;
+
+ set value(newValue) {
+ _observer.value = newValue;
+ }
+
+ NodeBinding(this._node, this.property, this._model, this.path) {
+ // Create the path observer
+ _observer = new PathObserver(model, path);
+ _observePath();
+ }
+
+ void _observePath() {
+ _pathSub = _observer.bindSync(boundValueChanged);
+ }
+
+ /** Called when [value] changes to update the [node]. */
+ // TODO(jmesserly): the impl in MDV uses mirrors to set the property,
+ // but that isn't used except for specific known fields like "textContent",
+ // so I'm overridding this in the subclasses instead.
+ void boundValueChanged(newValue);
+
+ /** Called to sanitize the value before it is assigned into the property. */
+ sanitizeBoundValue(value) => value == null ? '' : '$value';
+
+ /**
+ * Called by [NodeBindExtension.unbind] to close this binding and unobserve
+ * the [path].
+ *
+ * This can be overridden in subclasses, but they must call `super.close()`
+ * to free associated resources. They must also check [closed] and return
+ * immediately if already closed.
+ */
+ void close() {
+ if (closed) return;
+
+ if (_pathSub != null) _pathSub.cancel();
+ _pathSub = null;
+ _observer = null;
+ _node = null;
+ _model = null;
+ }
+}
« 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