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

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

Issue 11416259: fix #136, support watch exprs and two way bindings for component fields (Closed) Base URL: https://github.com/dart-lang/dart-web-components.git@master
Patch Set: Created 8 years 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
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 /** 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 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
95 void visitElementInfo(ElementInfo info) { 95 void visitElementInfo(ElementInfo info) {
96 var node = info.node; 96 var node = info.node;
97 97
98 if (node.id != '') info.identifier = '__${toCamelCase(node.id)}'; 98 if (node.id != '') info.identifier = '__${toCamelCase(node.id)}';
99 if (node.tagName == 'body' || (_currentInfo is ComponentInfo 99 if (node.tagName == 'body' || (_currentInfo is ComponentInfo
100 && (_currentInfo as ComponentInfo).template == node)) { 100 && (_currentInfo as ComponentInfo).template == node)) {
101 info.isRoot = true; 101 info.isRoot = true;
102 info.identifier = '_root'; 102 info.identifier = '_root';
103 } 103 }
104 104
105 node.attributes.forEach((k, v) => visitAttribute(info, k, v));
106
107 _bindCustomElement(node, info); 105 _bindCustomElement(node, info);
108 106
109 var lastInfo = _currentInfo; 107 var lastInfo = _currentInfo;
110 if (node.tagName == 'element') { 108 if (node.tagName == 'element') {
111 // If element is invalid _ElementLoader already reported an error, but 109 // If element is invalid _ElementLoader already reported an error, but
112 // we skip the body of the element here. 110 // we skip the body of the element here.
113 var name = node.attributes['name']; 111 var name = node.attributes['name'];
114 if (name == null) return; 112 if (name == null) return;
115 var component = _fileInfo.components[name]; 113 var component = _fileInfo.components[name];
116 if (component == null) return; 114 if (component == null) return;
117 115
118 // Associate ElementInfo of the <element> tag with its component. 116 // Associate ElementInfo of the <element> tag with its component.
119 component.elemInfo = info; 117 component.elemInfo = info;
120 118
121 _bindExtends(component); 119 _bindExtends(component);
122 120
123 _currentInfo = component; 121 _currentInfo = component;
124 } 122 }
125 123
124 node.attributes.forEach((k, v) => visitAttribute(info, k, v));
Jennifer Messerly 2012/11/29 05:34:35 needs to happen after we bind "extends"
125
126 var savedParent = _parent; 126 var savedParent = _parent;
127 _parent = info; 127 _parent = info;
128 128
129 // Invoke super to visit children. 129 // Invoke super to visit children.
130 super.visitElement(node); 130 super.visitElement(node);
131 _currentInfo = lastInfo; 131 _currentInfo = lastInfo;
132 132
133 _parent = savedParent; 133 _parent = savedParent;
134 134
135 if (_needsIdentifier(info)) { 135 if (_needsIdentifier(info)) {
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
331 attrInfo = _readAttribute(info, name, value); 331 attrInfo = _readAttribute(info, name, value);
332 } 332 }
333 333
334 if (attrInfo != null) { 334 if (attrInfo != null) {
335 info.attributes[name] = attrInfo; 335 info.attributes[name] = attrInfo;
336 info.hasDataBinding = true; 336 info.hasDataBinding = true;
337 } 337 }
338 } 338 }
339 339
340 bool _readDataValue(ElementInfo info, String value) { 340 bool _readDataValue(ElementInfo info, String value) {
341 messages.warning('data-value is deprecated. '
342 'Given data-value="fieldName:expr", replace it with '
343 'field-name="{{expr}}". Unlike data-value "expr" will be watched and '
Siggi Cherem (dart-lang) 2012/11/29 18:06:57 add comma? Unlike data-value, "expr" ...
Jennifer Messerly 2012/11/30 03:21:41 Done.
344 'fieldName will automatically update. You may also use '
345 'bind-field-name="dartAssignableValue" to get two-way data binding.',
346 info.node.sourceSpan, file: _fileInfo.path);
347
341 var colonIdx = value.indexOf(':'); 348 var colonIdx = value.indexOf(':');
342 if (colonIdx <= 0) { 349 if (colonIdx <= 0) {
343 messages.error('data-value attribute should be of the form ' 350 messages.error('data-value attribute should be of the form '
344 'data-value="name:value" or data-value=' 351 'data-value="name:value" or data-value='
345 '"name1:value1,name2:value2,..." for multiple assigments.', 352 '"name1:value1,name2:value2,..." for multiple assigments.',
346 info.node.sourceSpan, file: _fileInfo.path); 353 info.node.sourceSpan, file: _fileInfo.path);
347 return false; 354 return false;
348 } 355 }
349 var name = value.substring(0, colonIdx); 356 var name = value.substring(0, colonIdx);
350 value = value.substring(colonIdx + 1); 357 value = value.substring(colonIdx + 1);
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
422 var name = value.substring(0, colonIdx); 429 var name = value.substring(0, colonIdx);
423 value = value.substring(colonIdx + 1); 430 value = value.substring(colonIdx + 1);
424 431
425 return _readTwoWayBinding(info, name, value); 432 return _readTwoWayBinding(info, name, value);
426 } 433 }
427 434
428 // http://dev.w3.org/html5/spec/the-input-element.html#the-input-element 435 // http://dev.w3.org/html5/spec/the-input-element.html#the-input-element
429 /** Support for two-way bindings. */ 436 /** Support for two-way bindings. */
430 bool _readTwoWayBinding(ElementInfo info, String name, String bindingExpr) { 437 bool _readTwoWayBinding(ElementInfo info, String name, String bindingExpr) {
431 var elem = info.node; 438 var elem = info.node;
432 var isInput = elem.tagName == 'input'; 439
433 var isTextArea = elem.tagName == 'textarea'; 440 // Find the HTML tag name.
434 var isSelect = elem.tagName == 'select'; 441 var isInput = info.baseTagName == 'input';
442 var isTextArea = info.baseTagName == 'textarea';
443 var isSelect = info.baseTagName == 'select';
435 var inputType = elem.attributes['type']; 444 var inputType = elem.attributes['type'];
436 445
437 String eventName; 446 String eventName;
438 447
439 // Special two-way binding logic for input elements. 448 // Special two-way binding logic for input elements.
440 if (isInput && name == 'checked') { 449 if (isInput && name == 'checked') {
441 if (inputType == 'radio') { 450 if (inputType == 'radio') {
442 if (!_isValidRadioButton(info)) return false; 451 if (!_isValidRadioButton(info)) return false;
443 } else if (inputType != 'checkbox') { 452 } else if (inputType != 'checkbox') {
444 messages.error('checked is only supported in HTML with type="radio" ' 453 messages.error('checked is only supported in HTML with type="radio" '
445 'or type="checked".', info.node.sourceSpan, file: _fileInfo.path); 454 'or type="checked".', info.node.sourceSpan, file: _fileInfo.path);
446 return false; 455 return false;
447 } 456 }
448 457
449 // Both 'click' and 'change' seem reliable on all the modern browsers. 458 // Both 'click' and 'change' seem reliable on all the modern browsers.
450 eventName = 'change'; 459 eventName = 'change';
451 } else if (isSelect && (name == 'selectedIndex' || name == 'value')) { 460 } else if (isSelect && (name == 'selectedIndex' || name == 'value')) {
452 eventName = 'change'; 461 eventName = 'change';
453 } else if (isInput && name == 'value' && inputType == 'radio') { 462 } else if (isInput && name == 'value' && inputType == 'radio') {
454 return _addRadioValueBinding(info, bindingExpr); 463 return _addRadioValueBinding(info, bindingExpr);
455 } else if (isTextArea && name == 'value' || isInput && 464 } else if (isTextArea && name == 'value' || isInput &&
456 (name == 'value' || name == 'valueAsDate' || name == 'valueAsNumber')) { 465 (name == 'value' || name == 'valueAsDate' || name == 'valueAsNumber')) {
457 // Input event is fired more frequently than "change" on some browsers. 466 // Input event is fired more frequently than "change" on some browsers.
458 // We want to update the value for each keystroke. 467 // We want to update the value for each keystroke.
459 eventName = 'input'; 468 eventName = 'input';
469 } else if (info.component != null) {
470 // Assume we are binding a field on the component.
471 // TODO(jmesserly): validate this assumption about the user's code by
472 // using compile time mirrors.
473
474 _checkDuplicateAttribute(info, name);
475 info.attributes[name] = new AttributeInfo([bindingExpr],
476 customTwoWayBinding: true);
477 info.hasDataBinding = true;
478 return true;
479
460 } else { 480 } else {
461 messages.error('Unknown two-way binding attribute $name. Ignored.', 481 messages.error('Unknown two-way binding attribute $name. Ignored.',
462 info.node.sourceSpan, file: _fileInfo.path); 482 info.node.sourceSpan, file: _fileInfo.path);
463 return false; 483 return false;
464 } 484 }
465 485
466 if (elem.attributes[name] != null) { 486 _checkDuplicateAttribute(info, name);
467 messages.warning('Duplicate attribute $name. You should provide either '
468 'the two-way binding or the attribute itself. The attribute will be '
469 'ignored.', info.node.sourceSpan, file: _fileInfo.path);
470 info.removeAttributes.add(name);
471 }
472 487
473 info.attributes[name] = new AttributeInfo([bindingExpr]); 488 info.attributes[name] = new AttributeInfo([bindingExpr]);
474 _addEvent(info, eventName, (e) => '$bindingExpr = $e.$name'); 489 _addEvent(info, eventName, (e) => '$bindingExpr = $e.$name');
475 info.hasDataBinding = true; 490 info.hasDataBinding = true;
476 return true; 491 return true;
477 } 492 }
478 493
494 void _checkDuplicateAttribute(ElementInfo info, String name) {
495 if (info.node.attributes[name] != null) {
496 messages.warning('Duplicate attribute $name. You should provide either '
497 'the two-way binding or the attribute itself. The attribute will be '
498 'ignored.', info.node.sourceSpan, file: _fileInfo.path);
499 info.removeAttributes.add(name);
500 }
501 }
502
479 bool _isValidRadioButton(ElementInfo info) { 503 bool _isValidRadioButton(ElementInfo info) {
480 if (info.attributes['checked'] == null) return true; 504 if (info.attributes['checked'] == null) return true;
481 505
482 messages.error('Radio buttons cannot have both "checked" and "value" ' 506 messages.error('Radio buttons cannot have both "checked" and "value" '
483 'two-way bindings. Either use checked:\n' 507 'two-way bindings. Either use checked:\n'
484 ' <input type="radio" bind-checked="myBooleanVar">\n' 508 ' <input type="radio" bind-checked="myBooleanVar">\n'
485 'or value:\n' 509 'or value:\n'
486 ' <input type="radio" bind-value="myStringVar" value="theValue">', 510 ' <input type="radio" bind-value="myStringVar" value="theValue">',
487 info.node.sourceSpan, file: _fileInfo.path); 511 info.node.sourceSpan, file: _fileInfo.path);
488 return false; 512 return false;
(...skipping 404 matching lines...) Expand 10 before | Expand all | Expand 10 after
893 end = text.indexOf('}}', start); 917 end = text.indexOf('}}', start);
894 if (end < 0) { 918 if (end < 0) {
895 start = length; 919 start = length;
896 return false; 920 return false;
897 } 921 }
898 // For consistency, start and end both include the curly braces. 922 // For consistency, start and end both include the curly braces.
899 end += 2; 923 end += 2;
900 return true; 924 return true;
901 } 925 }
902 } 926 }
OLDNEW
« no previous file with comments | « example/todomvc/main.html ('k') | lib/src/emitters.dart » ('j') | lib/src/emitters.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698