| Index: lib/src/analyzer.dart
|
| diff --git a/lib/src/analyzer.dart b/lib/src/analyzer.dart
|
| index 4c071611c5d3fb8b301ba13ea03562bf0987bf8b..8150daee7232c7f8a15c61a562a8f18412646c47 100644
|
| --- a/lib/src/analyzer.dart
|
| +++ b/lib/src/analyzer.dart
|
| @@ -103,7 +103,8 @@ class _Analyzer extends TreeVisitor {
|
| || node.attributes.containsKey('template')
|
| || node.attributes.containsKey('if')
|
| || node.attributes.containsKey('instantiate')
|
| - || node.attributes.containsKey('iterate')) {
|
| + || node.attributes.containsKey('iterate')
|
| + || node.attributes.containsKey('repeat')) {
|
| // template tags, conditionals and iteration are handled specially.
|
| info = _createTemplateInfo(node);
|
| }
|
| @@ -284,7 +285,7 @@ class _Analyzer extends TreeVisitor {
|
| if (node.tagName != 'template' &&
|
| !node.attributes.containsKey('template')) {
|
| _messages.warning('template attribute is required when using if, '
|
| - 'instantiate, or iterate attributes.',
|
| + 'instantiate, repeat, or iterate attributes.',
|
| node.sourceSpan);
|
| }
|
|
|
| @@ -301,18 +302,28 @@ class _Analyzer extends TreeVisitor {
|
| }
|
| }
|
| }
|
| +
|
| + // TODO(jmesserly): deprecate iterate.
|
| var iterate = node.attributes['iterate'];
|
| + var repeat = node.attributes['repeat'];
|
| + if (repeat == null) {
|
| + repeat = iterate;
|
| + } else if (iterate != null) {
|
| + _messages.warning('template cannot have both iterate and repeat. '
|
| + 'iterate attribute will be ignored.', node.sourceSpan);
|
| + iterate = null;
|
| + }
|
|
|
| // Note: we issue warnings instead of errors because the spirit of HTML and
|
| // Dart is to be forgiving.
|
| - if (condition != null && iterate != null) {
|
| + if (condition != null && repeat != null) {
|
| _messages.warning('template cannot have both iteration and conditional '
|
| 'attributes', node.sourceSpan);
|
| return null;
|
| }
|
|
|
| if (node.parent != null && node.parent.tagName == 'element' &&
|
| - (condition != null || iterate != null)) {
|
| + (condition != null || repeat != null)) {
|
|
|
| // TODO(jmesserly): would be cool if we could just refactor this, or offer
|
| // a quick fix in the Editor.
|
| @@ -322,8 +333,8 @@ class _Analyzer extends TreeVisitor {
|
| node.attributes.forEach((k, v) { nestedTemplate.attributes[k] = v; });
|
|
|
| _messages.warning('the <template> of a custom element does not support '
|
| - '"if" or "iterate". However, you can create another template node '
|
| - 'that is a child node, for example:\n'
|
| + '"if", "iterate" or "repeat". However, you can create another '
|
| + 'template node that is a child node, for example:\n'
|
| '${example.outerHtml}',
|
| node.parent.sourceSpan);
|
| return null;
|
| @@ -337,41 +348,59 @@ class _Analyzer extends TreeVisitor {
|
| return node.nodes.length > 0 ? result : null;
|
| }
|
|
|
| - result.removeAttributes.add('template');
|
| -
|
| - // TODO(jmesserly): if-conditions in attributes require injecting a
|
| - // placeholder node, and a real node which is a clone. We should
|
| - // consider a design where we show/hide the node instead (with care
|
| - // taken not to evaluate hidden bindings). That is more along the lines
|
| - // of AngularJS, and would have a cleaner DOM. See issue #142.
|
| - var contentNode = node.clone();
|
| - // Clear out the original attributes. This is nice to have, but
|
| - // necessary for ID because of issue #141.
|
| - node.attributes.clear();
|
| - contentNode.nodes.addAll(node.nodes);
|
| -
|
| - // Create a new ElementInfo that is a child of "result" -- the
|
| - // placeholder node. This will become result.contentInfo.
|
| - visitElementInfo(_createElementInfo(contentNode, result));
|
| + _createTemplateAttributePlaceholder(node, result);
|
| return result;
|
| - } else if (iterate != null) {
|
| - var match = new RegExp(r"(.*) in (.*)").firstMatch(iterate);
|
| - if (match != null) {
|
| - if (node.nodes.length == 0) return null;
|
| - var result = new TemplateInfo(node, _parent, loopVariable: match[1],
|
| - loopItems: match[2]);
|
| - result.removeAttributes.add('iterate');
|
| - if (node.tagName != 'template') result.removeAttributes.add('template');
|
| +
|
| + } else if (repeat != null) {
|
| + var match = new RegExp(r"(.*) in (.*)").firstMatch(repeat);
|
| + if (match == null) {
|
| + _messages.warning('template iterate/repeat must be of the form: '
|
| + 'repeat="variable in list", where "variable" is your variable name '
|
| + 'and "list" is the list of items.',
|
| + node.sourceSpan);
|
| + return null;
|
| + }
|
| +
|
| + if (node.nodes.length == 0) return null;
|
| + var result = new TemplateInfo(node, _parent, loopVariable: match[1],
|
| + loopItems: match[2], isRepeat: iterate == null);
|
| + result.removeAttributes.add('iterate');
|
| + result.removeAttributes.add('repeat');
|
| + if (node.tagName == 'template') {
|
| return result;
|
| }
|
| - _messages.warning('template iterate must be of the form: '
|
| - 'iterate="variable in list", where "variable" is your variable name '
|
| - 'and "list" is the list of items.',
|
| - node.sourceSpan);
|
| +
|
| + if (!result.isRepeat) {
|
| + result.removeAttributes.add('template');
|
| + // TODO(jmesserly): deprecate this? I think you want "template repeat"
|
| + // most of the time, but "template iterate" seems useful sometimes.
|
| + // (Native <template> element parsing would make both obsolete, though.)
|
| + return result;
|
| + }
|
| +
|
| + _createTemplateAttributePlaceholder(node, result);
|
| + return result;
|
| }
|
| +
|
| return null;
|
| }
|
|
|
| + // TODO(jmesserly): if and repeat in attributes require injecting a
|
| + // placeholder node, and a real node which is a clone. We should
|
| + // consider a design where we show/hide the node instead (with care
|
| + // taken not to evaluate hidden bindings). That is more along the lines
|
| + // of AngularJS, and would have a cleaner DOM. See issue #142.
|
| + void _createTemplateAttributePlaceholder(Element node, TemplateInfo result) {
|
| + result.removeAttributes.add('template');
|
| + var contentNode = node.clone();
|
| + node.attributes.clear();
|
| + contentNode.nodes.addAll(node.nodes);
|
| +
|
| + // Create a new ElementInfo that is a child of "result" -- the
|
| + // placeholder node. This will become result.contentInfo.
|
| + visitElementInfo(_createElementInfo(contentNode, result));
|
| + }
|
| +
|
| void visitAttribute(ElementInfo info, String name, String value) {
|
| if (name.startsWith('on')) {
|
| _readEventHandler(info, name, value);
|
|
|