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

Side by Side Diff: lib/src/emitters.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
« no previous file with comments | « lib/src/analyzer.dart ('k') | lib/src/info.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 /** Collects several code emitters for the template tool. */ 5 /** Collects several code emitters for the template tool. */
6 // TODO(sigmund): add visitor that applies all emitters on a component 6 // TODO(sigmund): add visitor that applies all emitters on a component
7 // TODO(sigmund): add support for conditionals, so context is changed at that 7 // TODO(sigmund): add support for conditionals, so context is changed at that
8 // point. 8 // point.
9 library emitters; 9 library emitters;
10 10
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 /** Generates a field for any data-bound content node. */ 175 /** Generates a field for any data-bound content node. */
176 class ContentFieldEmitter extends Emitter<TextInfo> { 176 class ContentFieldEmitter extends Emitter<TextInfo> {
177 ContentFieldEmitter(TextInfo info) : super(info); 177 ContentFieldEmitter(TextInfo info) : super(info);
178 178
179 void emitDeclarations(Context context) { 179 void emitDeclarations(Context context) {
180 context.declarations.add('var ${info.identifier};'); 180 context.declarations.add('var ${info.identifier};');
181 } 181 }
182 182
183 void emitCreated(Context context) { 183 void emitCreated(Context context) {
184 context.createdMethod.add( 184 context.createdMethod.add(
185 '${info.identifier} = autogenerated.nodeForBinding(${info.binding});'); 185 "${info.identifier} = new autogenerated.Text('');");
186 } 186 }
187 187
188 void emitRemoved(Context context) { 188 void emitRemoved(Context context) {
189 context.removedMethod.add("${info.identifier} = null;"); 189 context.removedMethod.add("${info.identifier} = null;");
190 } 190 }
191 } 191 }
192 192
193 193
194 /** 194 /**
195 * Generates event listeners attached to a node and code that attaches/detaches 195 * Generates event listeners attached to a node and code that attaches/detaches
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 void _emitStyleAttributeInserted( 319 void _emitStyleAttributeInserted(
320 String stoppers, AttributeInfo attr, CodePrinter printer) { 320 String stoppers, AttributeInfo attr, CodePrinter printer) {
321 var id = info.identifier; 321 var id = info.identifier;
322 var binding = attr.boundValue; 322 var binding = attr.boundValue;
323 printer.add('$stoppers.add(autogenerated.bindStyle($id, () => $binding));'); 323 printer.add('$stoppers.add(autogenerated.bindStyle($id, () => $binding));');
324 } 324 }
325 325
326 void _emitSimpleAttributeInserted( 326 void _emitSimpleAttributeInserted(
327 String stoppers, String name, AttributeInfo attr, CodePrinter printer) { 327 String stoppers, String name, AttributeInfo attr, CodePrinter printer) {
328 var binding = attr.boundValue; 328 var binding = attr.boundValue;
329 var setter = _findDomSetter(info.node, name); 329 var field = _findDomField(info, name);
330 printer.add('$stoppers.add(autogenerated.watchAndInvoke(() => $binding, ' 330 printer.add('$stoppers.add(autogenerated.watchAndInvoke(() => $binding, '
331 '(__e) { ${info.identifier}.$setter = __e.newValue; }));'); 331 '(__e) { ${info.identifier}.$field = __e.newValue; }));');
332
333 if (attr.customTwoWayBinding) {
334 printer.add('$stoppers.add(autogenerated.watchAndInvoke(')
335 .add('() => ${info.identifier}.$field, ')
336 .add('(__e) { $binding = __e.newValue; }));');
337 }
332 } 338 }
333 339
334 void _emitTextAttributeInserted( 340 void _emitTextAttributeInserted(
335 String stoppers, String name, AttributeInfo attr, CodePrinter printer) { 341 String stoppers, String name, AttributeInfo attr, CodePrinter printer) {
336 var textContent = attr.textContent.map(escapeDartString); 342 var textContent = attr.textContent.map(escapeDartString);
337 var setter = _findDomSetter(info.node, name); 343 var setter = _findDomField(info, name);
338 var content = new StringBuffer(); 344 var content = new StringBuffer();
339 var binding; 345 var binding;
340 if (attr.bindings.length == 1) { 346 if (attr.bindings.length == 1) {
341 binding = attr.boundValue; 347 binding = attr.boundValue;
342 content.add(textContent[0]); 348 content.add(textContent[0]);
343 content.add('\${__e.newValue}'); 349 content.add('\${__e.newValue}');
344 content.add(textContent[1]); 350 content.add(textContent[1]);
345 } else { 351 } else {
346 // TODO(jmesserly): we could probably do something faster than a list 352 // TODO(jmesserly): we could probably do something faster than a list
347 // for watching on multiple bindings. 353 // for watching on multiple bindings.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
382 * 388 *
383 * And the component has been defined as: 389 * And the component has been defined as:
384 * 390 *
385 * <element name="x-hello" extends="div" constructor="HelloComponent"> 391 * <element name="x-hello" extends="div" constructor="HelloComponent">
386 * <template>Hello, <content>!</template> 392 * <template>Hello, <content>!</template>
387 * <script type="application/dart"></script> 393 * <script type="application/dart"></script>
388 * </element> 394 * </element>
389 * 395 *
390 * This will ensure that the Dart HelloComponent for `x-hello` is created and 396 * This will ensure that the Dart HelloComponent for `x-hello` is created and
391 * attached to the appropriate DOM node. 397 * attached to the appropriate DOM node.
392 *
393 * Also, this copies values from the scope into the object at component creation
394 * time, for example:
395 *
396 * <x-foo data-value="bar:baz">
397 *
398 * This will set the "bar" property of FooComponent to be "baz".
399 */ 398 */
400 class ComponentInstanceEmitter extends Emitter<ElementInfo> { 399 class ComponentInstanceEmitter extends Emitter<ElementInfo> {
401 ComponentInstanceEmitter(ElementInfo info) : super(info); 400 ComponentInstanceEmitter(ElementInfo info) : super(info);
402 401
403 void emitCreated(Context context) { 402 void emitCreated(Context context) {
404 var component = info.component; 403 var component = info.component;
405 if (component == null) return; 404 if (component == null) return;
406 405
407 var id = info.identifier; 406 var id = info.identifier;
408 context.createdMethod.add( 407 context.createdMethod.add('new ${component.constructor}.forElement($id)');
409 'var component$id = new ${component.constructor}.forElement($id);');
410 408
409 // Note: this feature is deprecated and will be removed.
411 info.values.forEach((name, value) { 410 info.values.forEach((name, value) {
412 context.createdMethod.add('component$id.$name = $value;'); 411 context.createdMethod.add('..$name = $value');
413 }); 412 });
414 413
415 context.createdMethod.add('component$id.created_autogenerated();') 414 context.createdMethod.add('..created_autogenerated()')
416 .add('component$id.created();') 415 .add('..created()')
417 .add('component$id.composeChildren();'); 416 .add('..composeChildren();');
418 } 417 }
419 418
420 void emitInserted(Context context) { 419 void emitInserted(Context context) {
421 if (info.component == null) return; 420 if (info.component == null) return;
422 421
423 // Note: watchers are intentionally hooked up after inserted() has run, 422 // Note: watchers are intentionally hooked up after inserted() has run,
424 // in case it makes any changes to the data. 423 // in case it makes any changes to the data.
425 var id = info.identifier; 424 var id = info.identifier;
426 context.insertedMethod.add('$id.xtag.inserted();') 425 context.insertedMethod.add('$id.xtag..inserted()')
427 .add('$id.xtag.inserted_autogenerated();'); 426 .add('..inserted_autogenerated();');
428 } 427 }
429 428
430 void emitRemoved(Context context) { 429 void emitRemoved(Context context) {
431 if (info.component == null) return; 430 if (info.component == null) return;
432 431
433 var id = info.identifier; 432 var id = info.identifier;
434 context.removedMethod.add('$id.xtag.removed_autogenerated();') 433 context.removedMethod.add('$id.xtag..removed_autogenerated()')
435 .add('$id.xtag.removed();'); 434 .add('..removed();');
436 } 435 }
437 } 436 }
438 437
439 /** 438 /**
440 * Emitter of template conditionals like `<template instantiate="if test">` or 439 * Emitter of template conditionals like `<template instantiate="if test">` or
441 * `<td template instantiate="if test">`. 440 * `<td template instantiate="if test">`.
442 * 441 *
443 * For a template element, we leave the (childless) template element in the 442 * For a template element, we leave the (childless) template element in the
444 * tree and use it as a reference point for child insertion. This matches 443 * tree and use it as a reference point for child insertion. This matches
445 * native MDV behavior. 444 * native MDV behavior.
(...skipping 465 matching lines...) Expand 10 before | Expand all | Expand 10 after
911 } 910 }
912 return 'new autogenerated.$constructor'; 911 return 'new autogenerated.$constructor';
913 } 912 }
914 913
915 /** 914 /**
916 * Finds the correct expression to set an HTML attribute through the DOM. 915 * Finds the correct expression to set an HTML attribute through the DOM.
917 * It is important for correctness to use the DOM setter if it is available. 916 * It is important for correctness to use the DOM setter if it is available.
918 * Otherwise changes will not be applied. This is most easily observed with 917 * Otherwise changes will not be applied. This is most easily observed with
919 * "InputElement.value", ".checked", etc. 918 * "InputElement.value", ".checked", etc.
920 */ 919 */
921 String _findDomSetter(Element node, String name) { 920 String _findDomField(ElementInfo info, String name) {
922 var typeName = typeForHtmlTag(node.tagName); 921 var typeName = typeForHtmlTag(info.baseTagName);
923 while (typeName != null) { 922 while (typeName != null) {
924 var fields = htmlElementFields[typeName]; 923 var fields = htmlElementFields[typeName];
925 if (fields != null) { 924 if (fields != null) {
926 var setter = fields[name]; 925 var field = fields[name];
927 if (setter != null) return setter; 926 if (field != null) return field;
928 } 927 }
929 typeName = htmlElementExtends[typeName]; 928 typeName = htmlElementExtends[typeName];
930 } 929 }
931 // If we didn't find a DOM setter, use the attributes map instead. 930 // If we didn't find a DOM setter, and this is a component, set a property on
932 return 'attributes["$name"]'; 931 // the component.
932 if (info.component != null && !name.startsWith('data-')) {
933 return 'xtag.$name';
934 }
935 return "attributes['$name']";
933 } 936 }
OLDNEW
« no previous file with comments | « lib/src/analyzer.dart ('k') | lib/src/info.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698