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

Side by Side Diff: lib/src/analyzer.dart

Issue 13592003: add support for template repeat (Closed) Base URL: https://github.com/dart-lang/web-ui.git@master
Patch Set: feedback Created 7 years, 8 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 | « no previous file | lib/src/emitters.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) 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 * Part of the template compilation that concerns with extracting information 6 * Part of the template compilation that concerns with extracting information
7 * from the HTML parse tree. 7 * from the HTML parse tree.
8 */ 8 */
9 library analyzer; 9 library analyzer;
10 10
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
96 var info = null; 96 var info = null;
97 if (node.tagName == 'script') { 97 if (node.tagName == 'script') {
98 // We already extracted script tags in previous phase. 98 // We already extracted script tags in previous phase.
99 return; 99 return;
100 } 100 }
101 101
102 if (node.tagName == 'template' 102 if (node.tagName == 'template'
103 || node.attributes.containsKey('template') 103 || node.attributes.containsKey('template')
104 || node.attributes.containsKey('if') 104 || node.attributes.containsKey('if')
105 || node.attributes.containsKey('instantiate') 105 || node.attributes.containsKey('instantiate')
106 || node.attributes.containsKey('iterate')) { 106 || node.attributes.containsKey('iterate')
107 || node.attributes.containsKey('repeat')) {
107 // template tags, conditionals and iteration are handled specially. 108 // template tags, conditionals and iteration are handled specially.
108 info = _createTemplateInfo(node); 109 info = _createTemplateInfo(node);
109 } 110 }
110 111
111 // TODO(jmesserly): it would be nice not to create infos for text or 112 // TODO(jmesserly): it would be nice not to create infos for text or
112 // elements that don't need data binding. Ideally, we would visit our 113 // elements that don't need data binding. Ideally, we would visit our
113 // child nodes and get their infos, and if any of them need data binding, 114 // child nodes and get their infos, and if any of them need data binding,
114 // we create an ElementInfo for ourselves and return it, otherwise we just 115 // we create an ElementInfo for ourselves and return it, otherwise we just
115 // return null. 116 // return null.
116 if (info == null) { 117 if (info == null) {
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
277 _currentInfo.usedComponents[component] = true; 278 _currentInfo.usedComponents[component] = true;
278 return component; 279 return component;
279 } 280 }
280 return null; 281 return null;
281 } 282 }
282 283
283 TemplateInfo _createTemplateInfo(Element node) { 284 TemplateInfo _createTemplateInfo(Element node) {
284 if (node.tagName != 'template' && 285 if (node.tagName != 'template' &&
285 !node.attributes.containsKey('template')) { 286 !node.attributes.containsKey('template')) {
286 _messages.warning('template attribute is required when using if, ' 287 _messages.warning('template attribute is required when using if, '
287 'instantiate, or iterate attributes.', 288 'instantiate, repeat, or iterate attributes.',
288 node.sourceSpan); 289 node.sourceSpan);
289 } 290 }
290 291
291 var instantiate = node.attributes['instantiate']; 292 var instantiate = node.attributes['instantiate'];
292 var condition = node.attributes['if']; 293 var condition = node.attributes['if'];
293 if (instantiate != null) { 294 if (instantiate != null) {
294 if (instantiate.startsWith('if ')) { 295 if (instantiate.startsWith('if ')) {
295 if (condition != null) { 296 if (condition != null) {
296 _messages.warning( 297 _messages.warning(
297 'another condition was already defined on this element.', 298 'another condition was already defined on this element.',
298 node.sourceSpan); 299 node.sourceSpan);
299 } else { 300 } else {
300 condition = instantiate.substring(3); 301 condition = instantiate.substring(3);
301 } 302 }
302 } 303 }
303 } 304 }
305
306 // TODO(jmesserly): deprecate iterate.
304 var iterate = node.attributes['iterate']; 307 var iterate = node.attributes['iterate'];
308 var repeat = node.attributes['repeat'];
309 if (repeat == null) {
310 repeat = iterate;
311 } else if (iterate != null) {
312 _messages.warning('template cannot have both iterate and repeat. '
313 'iterate attribute will be ignored.', node.sourceSpan);
314 iterate = null;
315 }
305 316
306 // Note: we issue warnings instead of errors because the spirit of HTML and 317 // Note: we issue warnings instead of errors because the spirit of HTML and
307 // Dart is to be forgiving. 318 // Dart is to be forgiving.
308 if (condition != null && iterate != null) { 319 if (condition != null && repeat != null) {
309 _messages.warning('template cannot have both iteration and conditional ' 320 _messages.warning('template cannot have both iteration and conditional '
310 'attributes', node.sourceSpan); 321 'attributes', node.sourceSpan);
311 return null; 322 return null;
312 } 323 }
313 324
314 if (node.parent != null && node.parent.tagName == 'element' && 325 if (node.parent != null && node.parent.tagName == 'element' &&
315 (condition != null || iterate != null)) { 326 (condition != null || repeat != null)) {
316 327
317 // TODO(jmesserly): would be cool if we could just refactor this, or offer 328 // TODO(jmesserly): would be cool if we could just refactor this, or offer
318 // a quick fix in the Editor. 329 // a quick fix in the Editor.
319 var example = new Element.html('<element><template><template>'); 330 var example = new Element.html('<element><template><template>');
320 node.parent.attributes.forEach((k, v) { example.attributes[k] = v; }); 331 node.parent.attributes.forEach((k, v) { example.attributes[k] = v; });
321 var nestedTemplate = example.nodes.first.nodes.first; 332 var nestedTemplate = example.nodes.first.nodes.first;
322 node.attributes.forEach((k, v) { nestedTemplate.attributes[k] = v; }); 333 node.attributes.forEach((k, v) { nestedTemplate.attributes[k] = v; });
323 334
324 _messages.warning('the <template> of a custom element does not support ' 335 _messages.warning('the <template> of a custom element does not support '
325 '"if" or "iterate". However, you can create another template node ' 336 '"if", "iterate" or "repeat". However, you can create another '
326 'that is a child node, for example:\n' 337 'template node that is a child node, for example:\n'
327 '${example.outerHtml}', 338 '${example.outerHtml}',
328 node.parent.sourceSpan); 339 node.parent.sourceSpan);
329 return null; 340 return null;
330 } 341 }
331 342
332 if (condition != null) { 343 if (condition != null) {
333 var result = new TemplateInfo(node, _parent, ifCondition: condition); 344 var result = new TemplateInfo(node, _parent, ifCondition: condition);
334 result.removeAttributes.add('if'); 345 result.removeAttributes.add('if');
335 result.removeAttributes.add('instantiate'); 346 result.removeAttributes.add('instantiate');
336 if (node.tagName == 'template') { 347 if (node.tagName == 'template') {
337 return node.nodes.length > 0 ? result : null; 348 return node.nodes.length > 0 ? result : null;
338 } 349 }
339 350
340 result.removeAttributes.add('template'); 351 _createTemplateAttributePlaceholder(node, result);
352 return result;
341 353
342 // TODO(jmesserly): if-conditions in attributes require injecting a 354 } else if (repeat != null) {
343 // placeholder node, and a real node which is a clone. We should 355 var match = new RegExp(r"(.*) in (.*)").firstMatch(repeat);
344 // consider a design where we show/hide the node instead (with care 356 if (match == null) {
345 // taken not to evaluate hidden bindings). That is more along the lines 357 _messages.warning('template iterate/repeat must be of the form: '
346 // of AngularJS, and would have a cleaner DOM. See issue #142. 358 'repeat="variable in list", where "variable" is your variable name '
347 var contentNode = node.clone(); 359 'and "list" is the list of items.',
348 // Clear out the original attributes. This is nice to have, but 360 node.sourceSpan);
349 // necessary for ID because of issue #141. 361 return null;
350 node.attributes.clear(); 362 }
351 contentNode.nodes.addAll(node.nodes);
352 363
353 // Create a new ElementInfo that is a child of "result" -- the 364 if (node.nodes.length == 0) return null;
354 // placeholder node. This will become result.contentInfo. 365 var result = new TemplateInfo(node, _parent, loopVariable: match[1],
355 visitElementInfo(_createElementInfo(contentNode, result)); 366 loopItems: match[2], isRepeat: iterate == null);
356 return result; 367 result.removeAttributes.add('iterate');
357 } else if (iterate != null) { 368 result.removeAttributes.add('repeat');
358 var match = new RegExp(r"(.*) in (.*)").firstMatch(iterate); 369 if (node.tagName == 'template') {
359 if (match != null) {
360 if (node.nodes.length == 0) return null;
361 var result = new TemplateInfo(node, _parent, loopVariable: match[1],
362 loopItems: match[2]);
363 result.removeAttributes.add('iterate');
364 if (node.tagName != 'template') result.removeAttributes.add('template');
365 return result; 370 return result;
366 } 371 }
367 _messages.warning('template iterate must be of the form: ' 372
368 'iterate="variable in list", where "variable" is your variable name ' 373 if (!result.isRepeat) {
369 'and "list" is the list of items.', 374 result.removeAttributes.add('template');
370 node.sourceSpan); 375 // TODO(jmesserly): deprecate this? I think you want "template repeat"
376 // most of the time, but "template iterate" seems useful sometimes.
377 // (Native <template> element parsing would make both obsolete, though.)
378 return result;
379 }
380
381 _createTemplateAttributePlaceholder(node, result);
382 return result;
371 } 383 }
384
372 return null; 385 return null;
373 } 386 }
374 387
388 // TODO(jmesserly): if and repeat in attributes require injecting a
389 // placeholder node, and a real node which is a clone. We should
390 // consider a design where we show/hide the node instead (with care
391 // taken not to evaluate hidden bindings). That is more along the lines
392 // of AngularJS, and would have a cleaner DOM. See issue #142.
393 void _createTemplateAttributePlaceholder(Element node, TemplateInfo result) {
394 result.removeAttributes.add('template');
395 var contentNode = node.clone();
396 node.attributes.clear();
397 contentNode.nodes.addAll(node.nodes);
398
399 // Create a new ElementInfo that is a child of "result" -- the
400 // placeholder node. This will become result.contentInfo.
401 visitElementInfo(_createElementInfo(contentNode, result));
402 }
403
375 void visitAttribute(ElementInfo info, String name, String value) { 404 void visitAttribute(ElementInfo info, String name, String value) {
376 if (name.startsWith('on')) { 405 if (name.startsWith('on')) {
377 _readEventHandler(info, name, value); 406 _readEventHandler(info, name, value);
378 return; 407 return;
379 } else if (name.startsWith('bind-')) { 408 } else if (name.startsWith('bind-')) {
380 // Strip leading "bind-" and make camel case. 409 // Strip leading "bind-" and make camel case.
381 var fieldName = toCamelCase(name.substring(5)); 410 var fieldName = toCamelCase(name.substring(5));
382 if (_readTwoWayBinding(info, fieldName, value)) { 411 if (_readTwoWayBinding(info, fieldName, value)) {
383 info.removeAttributes.add(name); 412 info.removeAttributes.add(name);
384 } 413 }
(...skipping 628 matching lines...) Expand 10 before | Expand all | Expand 10 after
1013 if (start == null) moveNext(); 1042 if (start == null) moveNext();
1014 if (start < length) { 1043 if (start < length) {
1015 do { 1044 do {
1016 bindings.add(binding); 1045 bindings.add(binding);
1017 content.add(textContent); 1046 content.add(textContent);
1018 } while (moveNext()); 1047 } while (moveNext());
1019 } 1048 }
1020 content.add(textContent); 1049 content.add(textContent);
1021 } 1050 }
1022 } 1051 }
OLDNEW
« no previous file with comments | « no previous file | lib/src/emitters.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698