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 template_binding; | 5 part of template_binding; |
| 6 | 6 |
| 7 /** Extensions to the [Node] API. */ | 7 /** Extensions to the [Node] API. */ |
| 8 class NodeBindExtension { | 8 class NodeBindExtension { |
| 9 final Node _node; | 9 final Node _node; |
| 10 final JsObject _js; | |
| 11 | |
| 12 NodeBindExtension._(node) | |
| 13 : _node = node, | |
| 14 _js = new JsObject.fromBrowserObject(node); | |
| 10 | 15 |
| 11 /** | 16 /** |
| 12 * Gets the data bindings that are associated with this node, if any. | 17 * Gets the data bindings that are associated with this node, if any. |
| 13 * | 18 * |
| 14 * This starts out null, and if [enableBindingsReflection] is enabled, calls | 19 * This starts out null, and if [enableBindingsReflection] is enabled, calls |
| 15 * to [bind] will initialize this field and the binding. | 20 * to [bind] will initialize this field and the binding. |
| 16 */ | 21 */ |
| 17 // Dart note: in JS this has a trailing underscore, meaning "private". | 22 // Dart note: in JS this has a trailing underscore, meaning "private". |
| 18 // But in dart if we made it _bindings, it wouldn't be accessible at all. | 23 // But in dart if we made it _bindings, it wouldn't be accessible at all. |
| 19 // It is unfortunately needed to implement Node.bind correctly. | 24 // It is unfortunately needed to implement Node.bind correctly. |
| 20 Map<String, Bindable> bindings; | 25 Map<String, Bindable> get bindings { |
| 21 | 26 var b = _js['bindings_']; |
| 22 NodeBindExtension._(this._node); | 27 if (b == null) return null; |
| 28 // TODO(jmesserly): should cache this for identity. | |
| 29 return new _NodeBindingsMap(b); | |
| 30 } | |
| 31 set bindings(Map<String, Bindable> value) { | |
|
Siggi Cherem (dart-lang)
2014/06/27 18:40:52
nit: +empty line above
Jennifer Messerly
2014/06/27 19:10:03
Done.
| |
| 32 if (value == null) { | |
| 33 _js.deleteProperty('bindings_'); | |
| 34 return; | |
| 35 } | |
| 36 var b = bindings; | |
| 37 if (b == null) { | |
| 38 _js['bindings_'] = new JsObject.jsify({}); | |
| 39 b = bindings; | |
| 40 } | |
| 41 b.addAll(value); | |
| 42 } | |
| 23 | 43 |
| 24 /** | 44 /** |
| 25 * Binds the attribute [name] to the [path] of the [model]. | 45 * Binds the attribute [name] to the [path] of the [model]. |
|
Siggi Cherem (dart-lang)
2014/06/27 18:40:53
I independently sent you a CL to update this outda
Jennifer Messerly
2014/06/27 19:10:03
lgtm!
| |
| 26 * Path is a String of accessors such as `foo.bar.baz`. | 46 * Path is a String of accessors such as `foo.bar.baz`. |
| 27 * Returns the [Bindable] instance. | 47 * Returns the [Bindable] instance. |
| 28 */ | 48 */ |
| 29 Bindable bind(String name, value, {bool oneTime: false}) { | 49 Bindable bind(String name, value, {bool oneTime: false}) { |
| 30 // TODO(jmesserly): in Dart we could deliver an async error, which would | 50 name = _dartToJsName(name); |
| 31 // have a similar affect but be reported as a test failure. Should we? | 51 |
| 32 window.console.error('Unhandled binding to Node: ' | 52 if (!oneTime && value is Bindable) { |
| 33 '$this $name $value $oneTime'); | 53 value = bindableToJsObject(value); |
| 34 return null; | 54 } |
| 55 return jsObjectToBindable(_js.callMethod('bind', [name, value, oneTime])); | |
| 35 } | 56 } |
| 36 | 57 |
| 37 /** | 58 /** |
| 38 * Called when all [bind] calls are finished for a given template expansion. | 59 * Called when all [bind] calls are finished for a given template expansion. |
| 39 */ | 60 */ |
| 40 void bindFinished() {} | 61 bindFinished() => _js.callMethod('bindFinished'); |
| 41 | 62 |
| 42 /** | 63 // Note: confusingly this is on NodeBindExtension because it can be on any |
| 43 * Dispatch support so custom HtmlElement's can override these methods. | 64 // Node. It's really an API added by TemplateBinding. Therefore it stays |
| 44 * | 65 // implemented in Dart because TemplateBinding still is. |
| 45 * A public method like [this.bind] should not call another public method. | |
| 46 * | |
| 47 * Instead it should dispatch through [_self] to give the "overridden" method | |
| 48 * a chance to intercept. | |
| 49 */ | |
| 50 NodeBindExtension get _self => _node is NodeBindExtension ? _node : this; | |
| 51 | |
| 52 TemplateInstance _templateInstance; | 66 TemplateInstance _templateInstance; |
| 53 | 67 |
| 54 /** Gets the template instance that instantiated this node, if any. */ | 68 /** Gets the template instance that instantiated this node, if any. */ |
| 55 TemplateInstance get templateInstance => | 69 TemplateInstance get templateInstance => |
| 56 _templateInstance != null ? _templateInstance : | 70 _templateInstance != null ? _templateInstance : |
| 57 (_node.parent != null ? nodeBind(_node.parent).templateInstance : null); | 71 (_node.parent != null ? nodeBind(_node.parent).templateInstance : null); |
| 72 } | |
| 58 | 73 |
| 59 _open(Bindable bindable, callback(value)) => | 74 class _NodeBindingsMap extends MapBase<String, Bindable> { |
| 60 callback(bindable.open(callback)); | 75 final JsObject _bindings; |
| 61 | 76 |
| 62 Bindable _maybeUpdateBindings(String name, Bindable binding) { | 77 _NodeBindingsMap(this._bindings); |
| 63 return enableBindingsReflection ? _updateBindings(name, binding) : binding; | 78 |
| 79 // TODO(jmesserly): this should be lazy | |
| 80 Iterable<String> get keys => | |
| 81 js.context['Object'].callMethod('keys', [_bindings]).map(_jsToDartName); | |
| 82 | |
| 83 Bindable operator[](String name) => | |
| 84 jsObjectToBindable(_bindings[_dartToJsName(name)]); | |
| 85 | |
| 86 operator[]=(String name, Bindable value) { | |
| 87 _bindings[_dartToJsName(name)] = bindableToJsObject(value); | |
| 64 } | 88 } |
| 65 | 89 |
| 66 Bindable _updateBindings(String name, Bindable binding) { | 90 @override Bindable remove(String name) { |
| 67 if (bindings == null) bindings = {}; | 91 name = _dartToJsName(name); |
| 68 var old = bindings[name]; | 92 var old = this[name]; |
| 69 if (old != null) old.close(); | 93 _bindings.deleteProperty(name); |
| 70 return bindings[name] = binding; | 94 return old; |
| 95 } | |
| 96 | |
| 97 @override void clear() { | |
| 98 keys.forEach(remove); | |
|
Siggi Cherem (dart-lang)
2014/06/27 18:40:52
since this is pretty much private, could we instea
Jennifer Messerly
2014/06/27 19:10:04
that's an interesting idea. Certainly in practice,
Siggi Cherem (dart-lang)
2014/06/27 19:22:45
sgtm - let's just add a comment to not forget that
| |
| 71 } | 99 } |
| 72 } | 100 } |
| 73 | 101 |
| 102 // TODO(jmesserly): perhaps we should switch Dart back to 'textContent' for | |
| 103 // consistency? This only affects the raw Node.bind API. Seems like a lot of | |
| 104 // magic to support it. | |
|
Siggi Cherem (dart-lang)
2014/06/27 18:40:53
not sure if it's worth to have this TODO here. May
Jennifer Messerly
2014/06/27 21:03:37
Oh -- definitely did *not* mean to suggest a chang
| |
| 105 String _dartToJsName(String name) { | |
|
Siggi Cherem (dart-lang)
2014/06/27 18:40:53
nit: how about just a 1 liner:
String _dartToJsNam
Jennifer Messerly
2014/06/27 21:03:36
Done.
| |
| 106 if (name == 'text') name = 'textContent'; | |
| 107 return name; | |
| 108 } | |
| 109 | |
| 110 | |
| 111 String _jsToDartName(String name) { | |
| 112 if (name == 'textContent') name = 'text'; | |
| 113 return name; | |
| 114 } | |
| 115 | |
| 116 | |
| 117 /// Given a bindable [JsObject], wraps it in a Dart [Bindable]. | |
| 118 /// See [bindableToJsObject] to go in the other direction. | |
| 119 Bindable jsObjectToBindable(JsObject obj) { | |
| 120 if (obj == null) return null; | |
| 121 var b = obj['__dartBindable']; | |
| 122 return b is Bindable ? b : new _JsBindable(obj); | |
|
Siggi Cherem (dart-lang)
2014/06/27 18:40:53
could 'b' ever be non-null & not Bindable? How abo
Jennifer Messerly
2014/06/27 19:10:04
yeah. I think that's essentially:
return b !=
Siggi Cherem (dart-lang)
2014/06/27 19:22:45
makes sense. let's keep it as it is, maybe mention
Jennifer Messerly
2014/06/27 21:03:36
Done.
| |
| 123 } | |
| 124 | |
| 125 class _JsBindable extends Bindable { | |
| 126 final JsObject _js; | |
| 127 _JsBindable(JsObject obj) : _js = obj; | |
| 128 | |
| 129 open(callback) => _js.callMethod('open', [callback]); | |
| 130 | |
| 131 close() => _js.callMethod('close'); | |
| 132 | |
| 133 get value => _js.callMethod('discardChanges'); | |
| 134 | |
| 135 set value(newValue) { | |
| 136 _js.callMethod('discardChanges'); | |
| 137 _js.callMethod('setValue', [newValue]); | |
| 138 } | |
| 139 | |
| 140 deliver() => _js.callMethod('deliver'); | |
| 141 } | |
| 142 | |
| 143 /// Given a [bindable], create a JS object proxy for it. | |
| 144 /// This is the inverse of [jsObjectToBindable]. | |
| 145 JsObject bindableToJsObject(Bindable bindable) { | |
| 146 if (bindable is _JsBindable) return bindable._js; | |
| 147 | |
| 148 return new JsObject.jsify({ | |
| 149 'open': (callback) => bindable.open((x) => callback.apply([x])), | |
| 150 'close': () => bindable.close(), | |
| 151 'discardChanges': () => bindable.value, | |
| 152 'setValue': (x) => bindable.value = x, | |
| 153 // NOTE: this is not used by Node.bind, but it's used by Polymer: | |
| 154 // https://github.com/Polymer/polymer-dev/blob/ba2b68fe5a5721f60b5994135f327 0e63588809a/src/declaration/properties.js#L130 | |
|
Siggi Cherem (dart-lang)
2014/06/27 18:40:53
would this be an issue anymore if we had the js-ex
Jennifer Messerly
2014/06/27 21:03:36
That's a really good point. We could actually remo
| |
| 155 'deliver': () => bindable.deliver(), | |
| 156 // Save this so we can return it from [jsObjectToBindable] | |
| 157 '__dartBindable': bindable | |
| 158 }); | |
| 159 } | |
| 74 | 160 |
| 75 /** Information about the instantiated template. */ | 161 /** Information about the instantiated template. */ |
| 76 class TemplateInstance { | 162 class TemplateInstance { |
| 77 // TODO(rafaelw): firstNode & lastNode should be read-synchronous | 163 // TODO(rafaelw): firstNode & lastNode should be read-synchronous |
| 78 // in cases where script has modified the template instance boundary. | 164 // in cases where script has modified the template instance boundary. |
| 79 | 165 |
| 80 /** The first node of this template instantiation. */ | 166 /** The first node of this template instantiation. */ |
| 81 Node get firstNode => _firstNode; | 167 Node get firstNode => _firstNode; |
| 82 | 168 |
| 83 /** | 169 /** |
| 84 * The last node of this template instantiation. | 170 * The last node of this template instantiation. |
| 85 * This could be identical to [firstNode] if the template only expanded to a | 171 * This could be identical to [firstNode] if the template only expanded to a |
| 86 * single node. | 172 * single node. |
| 87 */ | 173 */ |
| 88 Node get lastNode => _lastNode; | 174 Node get lastNode => _lastNode; |
| 89 | 175 |
| 90 /** The model used to instantiate the template. */ | 176 /** The model used to instantiate the template. */ |
| 91 final model; | 177 final model; |
| 92 | 178 |
| 93 Node _firstNode, _lastNode; | 179 Node _firstNode, _lastNode; |
| 94 | 180 |
| 95 TemplateInstance(this.model); | 181 TemplateInstance(this.model); |
| 96 } | 182 } |
| OLD | NEW |