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 /** | 5 /** |
6 * A binding delegate used with Polymer elements that | 6 * A binding delegate used with Polymer elements that |
7 * allows for complex binding expressions, including | 7 * allows for complex binding expressions, including |
8 * property access, function invocation, | 8 * property access, function invocation, |
9 * list/map indexing, and two-way filtering. | 9 * list/map indexing, and two-way filtering. |
10 * | 10 * |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
90 ? model | 90 ? model |
91 : _scopeFactory.modelScope(model: model, variables: globals); | 91 : _scopeFactory.modelScope(model: model, variables: globals); |
92 return new _Binding(bindExpr, scope); | 92 return new _Binding(bindExpr, scope); |
93 }; | 93 }; |
94 } else { | 94 } else { |
95 return (model, Node node, bool oneTime) { | 95 return (model, Node node, bool oneTime) { |
96 var scope = _scopes[node] = (model is Scope) | 96 var scope = _scopes[node] = (model is Scope) |
97 ? model | 97 ? model |
98 : _scopeFactory.modelScope(model: model, variables: globals); | 98 : _scopeFactory.modelScope(model: model, variables: globals); |
99 if (oneTime) { | 99 if (oneTime) { |
100 return _Binding._oneTime(expr, scope, null); | 100 return _Binding._oneTime(expr, scope); |
101 } | 101 } |
102 return new _Binding(expr, scope); | 102 return new _Binding(expr, scope); |
103 }; | 103 }; |
104 } | 104 } |
105 } | 105 } |
106 | 106 |
107 // For regular bindings, not bindings on a template, the model is always | 107 // For regular bindings, not bindings on a template, the model is always |
108 // a Scope created by prepareInstanceModel | 108 // a Scope created by prepareInstanceModel |
109 _Converter converter = null; | 109 _Converter converter = null; |
110 if (boundNode is Element && name == 'class') { | 110 if (boundNode is Element && name == 'class') { |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 } else { | 243 } else { |
244 // only happens in bindings_test | 244 // only happens in bindings_test |
245 scope = _scopeFactory.modelScope(model: model, variables: globals); | 245 scope = _scopeFactory.modelScope(model: model, variables: globals); |
246 } | 246 } |
247 return scope; | 247 return scope; |
248 } else { | 248 } else { |
249 return _getContainingScope(node.parentNode, model); | 249 return _getContainingScope(node.parentNode, model); |
250 } | 250 } |
251 } | 251 } |
252 | 252 |
| 253 /// Parse the expression string and return an expression tree. |
| 254 static Expression getExpression(String exprString) => |
| 255 new Parser(exprString).parse(); |
| 256 |
| 257 /// Determines the value of evaluating [expr] on the given [model] and returns |
| 258 /// either its value or a binding for it. If [oneTime] is true, it direclty |
| 259 /// returns the value. Otherwise, when [oneTime] is false, it returns a |
| 260 /// [Bindable] that besides evaluating the expression, it will also react to |
| 261 /// observable changes from the model and update the value accordingly. |
| 262 static getBinding(Expression expr, model, {Map<String, Object> globals, |
| 263 oneTime: false}) { |
| 264 if (globals == null) globals = new Map.from(DEFAULT_GLOBALS); |
| 265 var scope = model is Scope ? model |
| 266 : new Scope(model: model, variables: globals); |
| 267 return oneTime ? _Binding._oneTime(expr, scope) |
| 268 : new _Binding(expr, scope); |
| 269 } |
253 } | 270 } |
254 | 271 |
255 typedef Object _Converter(Object); | 272 typedef Object _Converter(Object); |
256 | 273 |
257 class _Binding extends Bindable { | 274 class _Binding extends Bindable { |
258 final Scope _scope; | 275 final Scope _scope; |
259 final _Converter _converter; | 276 final _Converter _converter; |
260 final Expression _expr; | 277 final Expression _expr; |
261 | 278 |
262 Function _callback; | 279 Function _callback; |
263 StreamSubscription _sub; | 280 StreamSubscription _sub; |
264 ExpressionObserver _observer; | 281 ExpressionObserver _observer; |
265 var _value; | 282 var _value; |
266 | 283 |
267 _Binding(this._expr, this._scope, [converter]) | 284 _Binding(this._expr, this._scope, [this._converter]); |
268 : _converter = converter == null ? _identity : converter; | |
269 | 285 |
270 static Object _oneTime(Expression expr, Scope scope, _Converter converter) { | 286 static Object _oneTime(Expression expr, Scope scope, [_Converter converter]) { |
271 try { | 287 try { |
272 var value = eval(expr, scope); | 288 var value = eval(expr, scope); |
273 return (converter == null) ? value : converter(value); | 289 return (converter == null) ? value : converter(value); |
274 } catch (e, s) { | 290 } catch (e, s) { |
275 new Completer().completeError( | 291 new Completer().completeError( |
276 "Error evaluating expression '$expr': $e", s); | 292 "Error evaluating expression '$expr': $e", s); |
277 } | 293 } |
278 return null; | 294 return null; |
279 } | 295 } |
280 | 296 |
281 bool _convertAndCheck(newValue, {bool skipChanges: false}) { | 297 bool _convertAndCheck(newValue, {bool skipChanges: false}) { |
282 var oldValue = _value; | 298 var oldValue = _value; |
283 _value = _converter(newValue); | 299 _value = _converter == null ? newValue : _converter(newValue); |
284 | 300 |
285 if (!skipChanges && _callback != null && oldValue != _value) { | 301 if (!skipChanges && _callback != null && oldValue != _value) { |
286 _callback(_value); | 302 _callback(_value); |
287 return true; | 303 return true; |
288 } | 304 } |
289 return false; | 305 return false; |
290 } | 306 } |
291 | 307 |
292 get value { | 308 get value { |
293 // if there's a callback, then _value has been set, if not we need to | 309 // if there's a callback, then _value has been set, if not we need to |
294 // force an evaluation | 310 // force an evaluation |
295 if (_callback != null) { | 311 if (_callback != null) { |
296 _check(skipChanges: true); | 312 _check(skipChanges: true); |
297 return _value; | 313 return _value; |
298 } | 314 } |
299 return _oneTime(_expr, _scope, _converter); | 315 return _Binding._oneTime(_expr, _scope, _converter); |
300 } | 316 } |
301 | 317 |
302 set value(v) { | 318 set value(v) { |
303 try { | 319 try { |
304 assign(_expr, v, _scope, checkAssignability: false); | 320 assign(_expr, v, _scope, checkAssignability: false); |
305 } catch (e, s) { | 321 } catch (e, s) { |
306 new Completer().completeError( | 322 new Completer().completeError( |
307 "Error evaluating expression '$_expr': $e", s); | 323 "Error evaluating expression '$_expr': $e", s); |
308 } | 324 } |
309 } | 325 } |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
372 * Factory function used for testing. | 388 * Factory function used for testing. |
373 */ | 389 */ |
374 class ScopeFactory { | 390 class ScopeFactory { |
375 const ScopeFactory(); | 391 const ScopeFactory(); |
376 modelScope({Object model, Map<String, Object> variables}) => | 392 modelScope({Object model, Map<String, Object> variables}) => |
377 new Scope(model: model, variables: variables); | 393 new Scope(model: model, variables: variables); |
378 | 394 |
379 childScope(Scope parent, String name, Object value) => | 395 childScope(Scope parent, String name, Object value) => |
380 parent.childScope(name, value); | 396 parent.childScope(name, value); |
381 } | 397 } |
OLD | NEW |