| // 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; |
| |
| /** |
| * Gets the data bindings that are associated with this node, if any. |
| * |
| * This starts out null, and if [enableBindingsReflection] is enabled, calls |
| * to [bind] will initialize this field and the binding. |
| */ |
| // Dart note: in JS this has a trailing underscore, meaning "private". |
| // But in dart if we made it _bindings, it wouldn't be accessible at all. |
| // It is unfortunately needed to implement Node.bind correctly. |
| Map<String, Bindable> bindings; |
| |
| NodeBindExtension._(this._node); |
| |
| /** |
| * Binds the attribute [name] to the [path] of the [model]. |
| * Path is a String of accessors such as `foo.bar.baz`. |
| * Returns the [Bindable] instance. |
| */ |
| Bindable bind(String name, value, {bool oneTime: false}) { |
| // TODO(jmesserly): in Dart we could deliver an async error, which would |
| // have a similar affect but be reported as a test failure. Should we? |
| window.console.error('Unhandled binding to Node: ' |
| '$this $name $value $oneTime'); |
| return null; |
| } |
| |
| /** |
| * Called when all [bind] calls are finished for a given template expansion. |
| */ |
| void bindFinished() {} |
| |
| /** |
| * Dispatch support so custom HtmlElement's can override these methods. |
| * |
| * A public method like [this.bind] should not call another public method. |
| * |
| * Instead it should dispatch through [_self] to give the "overridden" method |
| * a chance to intercept. |
| */ |
| NodeBindExtension get _self => _node is NodeBindExtension ? _node : this; |
| |
| TemplateInstance _templateInstance; |
| |
| /** Gets the template instance that instantiated this node, if any. */ |
| TemplateInstance get templateInstance => |
| _templateInstance != null ? _templateInstance : |
| (_node.parent != null ? nodeBind(_node.parent).templateInstance : null); |
| |
| _open(Bindable bindable, callback(value)) => |
| callback(bindable.open(callback)); |
| |
| Bindable _maybeUpdateBindings(String name, Bindable binding) { |
| return enableBindingsReflection ? _updateBindings(name, binding) : binding; |
| } |
| |
| Bindable _updateBindings(String name, Bindable binding) { |
| if (bindings == null) bindings = {}; |
| var old = bindings[name]; |
| if (old != null) old.close(); |
| return bindings[name] = binding; |
| } |
| } |
| |
| |
| /** 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. */ |
| Node get firstNode => _firstNode; |
| |
| /** |
| * The last node of this template instantiation. |
| * This could be identical to [firstNode] if the template only expanded to a |
| * single node. |
| */ |
| Node get lastNode => _lastNode; |
| |
| /** The model used to instantiate the template. */ |
| final model; |
| |
| Node _firstNode, _lastNode; |
| |
| TemplateInstance(this.model); |
| } |