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

Side by Side Diff: lib/templating.dart

Issue 12096106: work in progress: observable implementation using detailed change records (Closed) Base URL: https://github.com/dart-lang/web-ui.git@master
Patch Set: Created 7 years, 10 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
« no previous file with comments | « lib/src/utils.dart ('k') | lib/watcher.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /** Common utility functions used by code generated by the dwc compiler. */ 5 /** Common utility functions used by code generated by the dwc compiler. */
6 library templating; 6 library templating;
7 7
8 import 'dart:async'; 8 import 'dart:async';
9 import 'dart:html'; 9 import 'dart:html';
10 import 'dart:uri'; 10 import 'dart:uri';
11 import 'package:web_ui/safe_html.dart'; 11 import 'package:web_ui/safe_html.dart';
12 import 'package:web_ui/watcher.dart'; 12 import 'package:web_ui/watcher.dart';
13 import 'package:web_ui/observe.dart';
14 export 'src/utils.dart' show setImmediate;
13 15
14 /** 16 /**
15 * Take the value of a bound expression and creates an HTML node with its value. 17 * Take the value of a bound expression and creates an HTML node with its value.
16 * Normally bindings are associated with text nodes, unless [binding] has the 18 * Normally bindings are associated with text nodes, unless [binding] has the
17 * [SafeHtml] type, in which case an html element is created for it. 19 * [SafeHtml] type, in which case an html element is created for it.
18 */ 20 */
19 Node nodeForBinding(binding) => binding is SafeHtml 21 Node nodeForBinding(binding) => binding is SafeHtml
20 ? new Element.html(binding.toString()) : new Text(binding.toString()); 22 ? new Element.html(binding.toString()) : new Text(binding.toString());
21 23
22 /** 24 /**
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 * * binding classes with a string: 131 * * binding classes with a string:
130 * 132 *
131 * bindCssClasses(e, () => "${class1 != null ? class1 : ''} " 133 * bindCssClasses(e, () => "${class1 != null ? class1 : ''} "
132 * "${class2 != null ? class2 : ''}"); 134 * "${class2 != null ? class2 : ''}");
133 * 135 *
134 * * binding classes separately: 136 * * binding classes separately:
135 * 137 *
136 * bindCssClasses(e, () => class1); 138 * bindCssClasses(e, () => class1);
137 * bindCssClasses(e, () => class2); 139 * bindCssClasses(e, () => class2);
138 */ 140 */
139 WatcherDisposer bindCssClasses(Element elem, dynamic exp()) { 141 ChangeUnobserver bindCssClasses(Element elem, dynamic exp()) {
140 return watchAndInvoke(exp, (e) { 142 return watchAndInvoke(exp, (e) {
141 updateCssClass(elem, false, e.oldValue); 143 updateCssClass(elem, false, e.oldValue);
142 updateCssClass(elem, true, e.newValue); 144 updateCssClass(elem, true, e.newValue);
143 }); 145 });
144 } 146 }
145 147
146 /** Bind the result of [exp] to the style attribute in [elem]. */ 148 /** Bind the result of [exp] to the style attribute in [elem]. */
147 WatcherDisposer bindStyle(Element elem, Map<String, String> exp()) { 149 ChangeUnobserver bindStyle(Element elem, Map<String, String> exp()) {
148 return watchAndInvoke(exp, (e) { 150 return watchAndInvoke(exp, (e) {
149 if (e.oldValue is Map<String, String>) { 151 if (e.oldValue is Map<String, String>) {
150 var props = e.newValue; 152 var props = e.newValue;
151 if (props is! Map<String, String>) props = const {}; 153 if (props is! Map<String, String>) props = const {};
152 for (var property in e.oldValue.keys) { 154 for (var property in e.oldValue.keys) {
153 if (!props.containsKey(property)) { 155 if (!props.containsKey(property)) {
154 // Value will not be overwritten with new setting. Remove. 156 // Value will not be overwritten with new setting. Remove.
155 elem.style.removeProperty(property); 157 elem.style.removeProperty(property);
156 } 158 }
157 } 159 }
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 232
231 void remove() { 233 void remove() {
232 _subscription.cancel(); 234 _subscription.cancel();
233 _subscription = null; 235 _subscription = null;
234 } 236 }
235 } 237 }
236 238
237 /** Represents a generic data binding and a corresponding action. */ 239 /** Represents a generic data binding and a corresponding action. */
238 class Binding extends TemplateItem { 240 class Binding extends TemplateItem {
239 final exp; 241 final exp;
240 final ValueWatcher action; 242 final ChangeObserver action;
241 WatcherDisposer stopper; 243 ChangeUnobserver stopper;
242 244
243 Binding(this.exp, this.action); 245 Binding(this.exp, this.action);
244 246
245 void create() {} 247 void create() {}
246 void insert() { 248 void insert() {
247 if (stopper != null) throw new StateError('binding already attached'); 249 if (stopper != null) throw new StateError('binding already attached');
248 stopper = watchAndInvoke(exp, action); 250 stopper = watchAndInvoke(exp, action);
249 } 251 }
250 void remove() { 252 void remove() {
251 stopper(); 253 stopper();
252 stopper = null; 254 stopper = null;
253 } 255 }
254 } 256 }
255 257
256 /** Represents a binding to a style attribute. */ 258 /** Represents a binding to a style attribute. */
257 class StyleAttrBinding extends TemplateItem { 259 class StyleAttrBinding extends TemplateItem {
258 final exp; 260 final exp;
259 final Element elem; 261 final Element elem;
260 WatcherDisposer stopper; 262 ChangeUnobserver stopper;
261 263
262 StyleAttrBinding(this.elem, this.exp); 264 StyleAttrBinding(this.elem, this.exp);
263 265
264 void create() {} 266 void create() {}
265 void insert() { 267 void insert() {
266 if (stopper != null) throw new StateError('style binding already attached'); 268 if (stopper != null) throw new StateError('style binding already attached');
267 stopper = bindStyle(elem, exp); 269 stopper = bindStyle(elem, exp);
268 } 270 }
269 void remove() { 271 void remove() {
270 stopper(); 272 stopper();
271 stopper = null; 273 stopper = null;
272 } 274 }
273 } 275 }
274 276
275 /** Represents a binding to a class attribute. */ 277 /** Represents a binding to a class attribute. */
276 class ClassAttrBinding extends TemplateItem { 278 class ClassAttrBinding extends TemplateItem {
277 final Element elem; 279 final Element elem;
278 final exp; 280 final exp;
279 WatcherDisposer stopper; 281 ChangeUnobserver stopper;
280 282
281 ClassAttrBinding(this.elem, this.exp); 283 ClassAttrBinding(this.elem, this.exp);
282 284
283 void create() {} 285 void create() {}
284 void insert() { 286 void insert() {
285 if (stopper != null) throw new StateError('class binding already attached'); 287 if (stopper != null) throw new StateError('class binding already attached');
286 stopper = bindCssClasses(elem, exp); 288 stopper = bindCssClasses(elem, exp);
287 } 289 }
288 void remove() { 290 void remove() {
289 stopper(); 291 stopper();
(...skipping 14 matching lines...) Expand all
304 * or from a DOM property (which is internally also a Dart expression). 306 * or from a DOM property (which is internally also a Dart expression).
305 */ 307 */
306 final Getter getter; 308 final Getter getter;
307 309
308 /** 310 /**
309 * Whether this is a binding that assigns a DOM attribute accepting URL 311 * Whether this is a binding that assigns a DOM attribute accepting URL
310 * values. If so, the value assigned to the attribute needs to be sanitized. 312 * values. If so, the value assigned to the attribute needs to be sanitized.
311 */ 313 */
312 final bool isUrl; 314 final bool isUrl;
313 315
314 WatcherDisposer stopper; 316 ChangeUnobserver stopper;
315 317
316 DomPropertyBinding(this.getter, this.setter, this.isUrl); 318 DomPropertyBinding(this.getter, this.setter, this.isUrl);
317 319
318 void create() {} 320 void create() {}
319 void insert() { 321 void insert() {
320 if (stopper != null) throw new StateError('data binding already attached.'); 322 if (stopper != null) throw new StateError('data binding already attached.');
321 stopper = watchAndInvoke(getter, (e) { 323 stopper = watchAndInvoke(getter, (e) {
322 setter(isUrl ? sanitizeUri(e.newValue) : e.newValue); 324 setter(isUrl ? sanitizeUri(e.newValue) : e.newValue);
323 }); 325 });
324 } 326 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
368 final List<Node> nodes = []; 370 final List<Node> nodes = [];
369 371
370 Template(this.node); 372 Template(this.node);
371 373
372 /** Associate the event listener while this template is visible. */ 374 /** Associate the event listener while this template is visible. */
373 void listen(Stream<Event> stream, EventListener listener) { 375 void listen(Stream<Event> stream, EventListener listener) {
374 children.add(new Listener(stream, (e) { listener(e); dispatch(); })); 376 children.add(new Listener(stream, (e) { listener(e); dispatch(); }));
375 } 377 }
376 378
377 /** Run [action] when [exp] changes (while this template is visible). */ 379 /** Run [action] when [exp] changes (while this template is visible). */
378 void bind(exp, ValueWatcher action) { 380 void bind(exp, ChangeObserver action) {
379 children.add(new Binding(exp, action)); 381 children.add(new Binding(exp, action));
380 } 382 }
381 383
382 /** Create and bind a [Node] to [exp] while this template is visible. */ 384 /** Create and bind a [Node] to [exp] while this template is visible. */
383 Node contentBind(Function exp) { 385 Node contentBind(Function exp) {
384 var bindNode = new Text(''); 386 var bindNode = new Text('');
385 children.add(new Binding(() => '${exp()}', (e) { 387 children.add(new Binding(() => '${exp()}', (e) {
386 bindNode = updateBinding(exp(), bindNode, e.newValue); 388 bindNode = updateBinding(exp(), bindNode, e.newValue);
387 })); 389 }));
388 return bindNode; 390 return bindNode;
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
481 // </tr> 483 // </tr>
482 // 484 //
483 // We can't necessarily rely on child position because of possible mutation, 485 // We can't necessarily rely on child position because of possible mutation,
484 // unless we're willing to say that "if" requires a fixed number of children. 486 // unless we're willing to say that "if" requires a fixed number of children.
485 // If that's the case, we need a way to check for this error case and alert the 487 // If that's the case, we need a way to check for this error case and alert the
486 // developer. 488 // developer.
487 abstract class PlaceholderTemplate extends Template { 489 abstract class PlaceholderTemplate extends Template {
488 /** Expression watch by this template (condition or loop expression). */ 490 /** Expression watch by this template (condition or loop expression). */
489 final exp; 491 final exp;
490 492
491 WatcherDisposer stopper; 493 ChangeUnobserver stopper;
492 494
493 PlaceholderTemplate(Node reference, this.exp) 495 PlaceholderTemplate(Node reference, this.exp)
494 : super(reference); 496 : super(reference);
495 497
496 void create() {} 498 void create() {}
497 499
498 void insert() { 500 void insert() {
499 super.create(); 501 super.create();
500 if (nodes.length > 0) { 502 if (nodes.length > 0) {
501 var parent = node.parentNode; 503 var parent = node.parentNode;
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
582 } 584 }
583 585
584 /** 586 /**
585 * A template loop of the form `<td template iterate="x in list ">`. Unlike 587 * A template loop of the form `<td template iterate="x in list ">`. Unlike
586 * [LoopTemplate], here we insert children directly then node annotated with the 588 * [LoopTemplate], here we insert children directly then node annotated with the
587 * template attribute. 589 * template attribute.
588 */ 590 */
589 class LoopTemplateInAttribute extends Template { 591 class LoopTemplateInAttribute extends Template {
590 final LoopIterationSetup iterSetup; 592 final LoopIterationSetup iterSetup;
591 final exp; 593 final exp;
592 WatcherDisposer stopper; 594 ChangeUnobserver stopper;
593 595
594 LoopTemplateInAttribute(Node node, this.exp, this.iterSetup) : super(node); 596 LoopTemplateInAttribute(Node node, this.exp, this.iterSetup) : super(node);
595 597
596 void create() {} 598 void create() {}
597 599
598 void insert() { 600 void insert() {
599 stopper = watchAndInvoke(exp, (e) { 601 stopper = watchAndInvoke(exp, (e) {
600 _removeInternal(); 602 _removeInternal();
601 for (var x in e.newValue) { 603 for (var x in e.newValue) {
602 iterSetup(x, this); 604 iterSetup(x, this);
603 } 605 }
604 super.create(); 606 super.create();
605 node.nodes.addAll(nodes); 607 node.nodes.addAll(nodes);
606 super.insert(); 608 super.insert();
607 }); 609 });
608 } 610 }
609 611
610 void _removeInternal() { 612 void _removeInternal() {
611 super.remove(); 613 super.remove();
612 node.nodes.clear(); 614 node.nodes.clear();
613 nodes.clear(); 615 nodes.clear();
614 } 616 }
615 617
616 void remove() { 618 void remove() {
617 _removeInternal(); 619 _removeInternal();
618 stopper(); 620 stopper();
619 stopper = null; 621 stopper = null;
620 } 622 }
621 } 623 }
OLDNEW
« no previous file with comments | « lib/src/utils.dart ('k') | lib/watcher.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698