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

Side by Side Diff: pkg/template_binding/lib/src/template.dart

Issue 50203004: port TemplateBinding to ed3266266e751b5ab1f75f8e0509d0d5f0ef35d8 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 1 month 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 | Annotate | Revision Log
OLDNEW
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 [Element]s that behave as templates. */ 7 /** Extensions to [Element]s that behave as templates. */
8 class TemplateBindExtension extends _ElementExtension { 8 class TemplateBindExtension extends _ElementExtension {
9 var _model; 9 var _model;
10 BindingDelegate _bindingDelegate; 10 BindingDelegate _bindingDelegate;
11 _TemplateIterator _templateIterator; 11 _TemplateIterator _iterator;
12 bool _scheduled = false; 12 bool _scheduled = false;
13 13
14 Element _templateInstanceRef; 14 Element _templateInstanceRef;
15 15
16 // Note: only used if `this is! TemplateElement` 16 // Note: only used if `this is! TemplateElement`
17 DocumentFragment _content; 17 DocumentFragment _content;
18 bool _templateIsDecorated; 18 bool _templateIsDecorated;
19 19
20 var _bindingMap;
21
20 TemplateBindExtension._(Element node) : super(node); 22 TemplateBindExtension._(Element node) : super(node);
21 23
22 Element get _node => super._node; 24 Element get _node => super._node;
23 25
26 TemplateBindExtension get _self => super._node is TemplateBindExtension
27 ? _node : this;
28
24 NodeBinding bind(String name, model, [String path]) { 29 NodeBinding bind(String name, model, [String path]) {
30 path = path != null ? path : '';
31
32 if (_iterator == null) {
33 // TODO(jmesserly): since there's only one iterator, we could just
34 // inline it into this object.
35 _iterator = new _TemplateIterator(this);
36 }
37
38 // Dart note: we return _TemplateBinding instead of _iterator.
39 // See comment on _TemplateBinding class.
25 switch (name) { 40 switch (name) {
26 case 'bind': 41 case 'bind':
42 _iterator..hasBind = true
43 ..bindModel = model
44 ..bindPath = path;
45 _scheduleIterator();
46 return bindings[name] = new _TemplateBinding(this, name, model, path);
27 case 'repeat': 47 case 'repeat':
48 _iterator..hasRepeat = true
49 ..repeatModel = model
50 ..repeatPath = path;
51 _scheduleIterator();
52 return bindings[name] = new _TemplateBinding(this, name, model, path);
28 case 'if': 53 case 'if':
29 _self.unbind(name); 54 _iterator..hasIf = true
30 if (_templateIterator == null) { 55 ..ifModel = model
31 _templateIterator = new _TemplateIterator(_node); 56 ..ifPath = path;
32 } 57 _scheduleIterator();
33 return bindings[name] = new _TemplateBinding(this, name, model, path); 58 return bindings[name] = new _TemplateBinding(this, name, model, path);
34 default: 59 default:
35 return super.bind(name, model, path); 60 return super.bind(name, model, path);
36 } 61 }
37 } 62 }
38 63
64 void unbind(String name) {
65 switch (name) {
66 case 'bind':
67 if (_iterator == null) return;
68 _iterator..hasBind = false
69 ..bindModel = null
70 ..bindPath = null;
71 _scheduleIterator();
72 bindings.remove(name);
73 return;
74 case 'repeat':
75 if (_iterator == null) return;
76 _iterator..hasRepeat = false
77 ..repeatModel = null
78 ..repeatPath = null;
79 _scheduleIterator();
80 bindings.remove(name);
81 return;
82 case 'if':
83 if (_iterator == null) return;
84 _iterator..hasIf = false
85 ..ifModel = null
86 ..ifPath = null;
87 _scheduleIterator();
88 bindings.remove(name);
89 return;
90 default:
91 super.unbind(name);
92 return;
93 }
94 }
95
96 void _scheduleIterator() {
97 if (!_iterator.depsChanging) {
98 _iterator.depsChanging = true;
99 scheduleMicrotask(_iterator.resolve);
100 }
101 }
102
39 /** 103 /**
40 * Creates an instance of the template, using the provided model and optional 104 * Creates an instance of the template, using the provided model and optional
41 * binding delegate. 105 * binding delegate.
42 */ 106 */
43 DocumentFragment createInstance([model, BindingDelegate delegate]) { 107 DocumentFragment createInstance([model, BindingDelegate delegate,
44 var instance = _createDeepCloneAndDecorateTemplates( 108 List<NodeBinding> bound]) {
45 templateBind(ref).content, delegate); 109 var ref = templateBind(this.ref);
110 var content = ref.content;
111 // Dart note: we store _bindingMap on the TemplateBindExtension instead of
112 // the "content" because we already have an expando for it.
113 var map = ref._bindingMap;
114 if (map == null) {
115 // TODO(rafaelw): Setup a MutationObserver on content to detect
116 // when the instanceMap is invalid.
117 map = new _InstanceBindingMap(content, delegate);
118 ref._bindingMap = map;
119 }
46 120
47 _addBindings(instance, model, delegate); 121 var instance = map.hasSubTemplate
122 ? _deepCloneIgnoreTemplateContent(content)
123 : content.clone(true);
124
125 _addMapBindings(instance, map, model, delegate, bound);
126 // TODO(rafaelw): We can do this more lazily, but setting a sentinel
127 // in the parent of the template element, and creating it when it's
128 // asked for by walking back to find the iterating template.
48 _addTemplateInstanceRecord(instance, model); 129 _addTemplateInstanceRecord(instance, model);
49 return instance; 130 return instance;
50 } 131 }
51 132
52 /** The data model which is inherited through the tree. */ 133 /** The data model which is inherited through the tree. */
53 get model => _model; 134 get model => _model;
54 135
55 void set model(value) { 136 void set model(value) {
56 _model = value; 137 _model = value;
57 _ensureSetModelScheduled(); 138 _ensureSetModelScheduled();
58 } 139 }
59 140
141 static Node _deepCloneIgnoreTemplateContent(Node node) {
142 var clone = node.clone(false); // Shallow clone.
143 if (isSemanticTemplate(clone)) return clone;
144
145 for (var c = node.firstChild; c != null; c = c.nextNode) {
146 clone.append(_deepCloneIgnoreTemplateContent(c));
147 }
148 return clone;
149 }
150
60 /** 151 /**
61 * The binding delegate which is inherited through the tree. It can be used 152 * The binding delegate which is inherited through the tree. It can be used
62 * to configure custom syntax for `{{bindings}}` inside this template. 153 * to configure custom syntax for `{{bindings}}` inside this template.
63 */ 154 */
64 BindingDelegate get bindingDelegate => _bindingDelegate; 155 BindingDelegate get bindingDelegate => _bindingDelegate;
65 156
66 void set bindingDelegate(BindingDelegate value) { 157 void set bindingDelegate(BindingDelegate value) {
67 _bindingDelegate = value; 158 _bindingDelegate = value;
68 _ensureSetModelScheduled(); 159 _ensureSetModelScheduled();
69 } 160 }
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
131 222
132 var templateElementExt = this; 223 var templateElementExt = this;
133 _templateIsDecorated = true; 224 _templateIsDecorated = true;
134 var isNative = _node is TemplateElement; 225 var isNative = _node is TemplateElement;
135 var bootstrapContents = isNative; 226 var bootstrapContents = isNative;
136 var liftContents = !isNative; 227 var liftContents = !isNative;
137 var liftRoot = false; 228 var liftRoot = false;
138 229
139 if (!isNative && _isAttributeTemplate(_node)) { 230 if (!isNative && _isAttributeTemplate(_node)) {
140 if (instanceRef != null) { 231 if (instanceRef != null) {
141 // TODO(jmesserly): this is just an assert in MDV. 232 // TODO(jmesserly): this is just an assert in TemplateBinding.
142 throw new ArgumentError('instanceRef should not be supplied for ' 233 throw new ArgumentError('instanceRef should not be supplied for '
143 'attribute templates.'); 234 'attribute templates.');
144 } 235 }
145 templateElementExt = templateBind( 236 templateElementExt = templateBind(
146 _extractTemplateFromAttributeTemplate(_node)); 237 _extractTemplateFromAttributeTemplate(_node));
147 templateElementExt._templateIsDecorated = true; 238 templateElementExt._templateIsDecorated = true;
148 isNative = templateElementExt._node is TemplateElement; 239 isNative = templateElementExt._node is TemplateElement;
149 liftRoot = true; 240 liftRoot = true;
150 } 241 }
151 242
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
262 353
263 descendents.forEach(_bootstrap); 354 descendents.forEach(_bootstrap);
264 } 355 }
265 356
266 static final String _allTemplatesSelectors = 357 static final String _allTemplatesSelectors =
267 'template, ' + 358 'template, ' +
268 _SEMANTIC_TEMPLATE_TAGS.keys.map((k) => "$k[template]").join(", "); 359 _SEMANTIC_TEMPLATE_TAGS.keys.map((k) => "$k[template]").join(", ");
269 360
270 static bool _initStyles; 361 static bool _initStyles;
271 362
363 // This is to replicate template_element.css
364 // TODO(jmesserly): move this to an opt-in CSS file?
272 static void _injectStylesheet() { 365 static void _injectStylesheet() {
273 if (_initStyles == true) return; 366 if (_initStyles == true) return;
274 _initStyles = true; 367 _initStyles = true;
275 368
276 var style = new StyleElement(); 369 var style = new StyleElement()
277 style.text = r''' 370 ..text = '$_allTemplatesSelectors { display: none; }';
278 template,
279 thead[template],
280 tbody[template],
281 tfoot[template],
282 th[template],
283 tr[template],
284 td[template],
285 caption[template],
286 colgroup[template],
287 col[template],
288 option[template] {
289 display: none;
290 }''';
291 document.head.append(style); 371 document.head.append(style);
292 } 372 }
293 } 373 }
294 374
295 class _TemplateBinding extends NodeBinding { 375 // TODO(jmesserly): https://github.com/polymer/templatebinding uses
376 // TemplateIterator as the binding. This is a nice performance optimization,
377 // however it means it doesn't share any of the reflective APIs with
378 // NodeBinding: https://github.com/Polymer/TemplateBinding/issues/147
379 class _TemplateBinding implements NodeBinding {
296 TemplateBindExtension _ext; 380 TemplateBindExtension _ext;
381 Object _model;
382 final String property;
383 final String path;
297 384
298 // TODO(jmesserly): MDV uses TemplateIterator as the node, see: 385 Node get node => _ext._node;
299 // https://github.com/Polymer/mdv/issues/127 386
300 _TemplateBinding(ext, name, model, path) 387 get model => _model;
301 : _ext = ext, super(ext._node, name, model, path) { 388
302 _ext._templateIterator.inputs.bind(property, model, this.path); 389 bool get closed => _ext == null;
390
391 get value => _observer.value;
392
393 set value(newValue) {
394 _observer.value = newValue;
303 } 395 }
304 396
305 // These are no-ops because we don't use the underlying PathObserver. 397 // No need to cache this since we only have it to support get/set value.
306 void _observePath() {} 398 get _observer {
399 if ((_model is PathObserver || _model is CompoundPathObserver) &&
400 path == 'value') {
401 return _model;
402 }
403 return new PathObserver(_model, path);
404 }
405
406 _TemplateBinding(this._ext, this.property, this._model, this.path);
407
307 void valueChanged(newValue) {} 408 void valueChanged(newValue) {}
308 409
410 sanitizeBoundValue(value) => value == null ? '' : '$value';
411
309 void close() { 412 void close() {
310 if (closed) return; 413 if (closed) return;
311 var templateIterator = _ext._templateIterator; 414
312 if (templateIterator != null) templateIterator.inputs.unbind(property); 415 // TODO(jmesserly): unlike normal NodeBinding.close methods this will remove
313 super.close(); 416 // the binding from _node.bindings. Is that okay?
417 _ext.unbind(property);
418
419 _model = null;
420 _ext = null;
314 } 421 }
315 } 422 }
316 423
317 _getTreeScope(Node node) { 424 _getTreeScope(Node node) {
318 while (node.parentNode != null) { 425 while (node.parentNode != null) {
319 node = node.parentNode; 426 node = node.parentNode;
320 } 427 }
321 428
322 // Note: JS code tests that getElementById is present. We can't do that 429 // Note: JS code tests that getElementById is present. We can't do that
323 // easily, so instead check for the types known to implement it. 430 // easily, so instead check for the types known to implement it.
324 if (node is Document || node is ShadowRoot || node is SvgSvgElement) { 431 if (node is Document || node is ShadowRoot || node is SvgSvgElement) {
325 return node; 432 return node;
326 } 433 }
327 return null; 434 return null;
328 } 435 }
OLDNEW
« no previous file with comments | « pkg/template_binding/lib/src/node_binding.dart ('k') | pkg/template_binding/lib/src/template_iterator.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698