Now for the fun. Now for Dart.
I kid (a little). One of the joys of writing Patterns in Polymer is slipping back and forth between JavaScript and Dart. If I am being honest, I prefer Dart, but both languages have their joys. More than experiencing those joys, I appreciate trying solutions for Polymer in one language and then another. If a solution is pleasant in both languages, then I feel like I have found a nice solution that transcends language—making it a true Polymer pattern.
In some ways, switch back and forth between languages feels a little like spiking solutions, then doing it for real. In real programming that is loads of fun (not to mention is leads to better solutions). Surprisingly, I have found this to be a bad idea writing the book. It is easy to mistake tinghe post-spike flow for a transcendent pattern. Of course it works when switching languages, I already pushed through the gaps in knowledge in the other language.
Instead, I have been making a concerted effort to push the solution forward when switching between languages. When I got my initial spike of
<a-form-input>
(it allows Polymer elements to work in native HTML forms) working in Dart, I did not try to get it working in JavaScript. Instead I tried building a base class / element in JavaScript. Now that I have that working in JavaScript, I try something a little different in Dart. Instead of just making <a-form-input>
a base class in Dart, I am going to see if I can make it work with the is="a-form-input"
syntax.I start by reproducing my latest JavaScript work in Dart. That is, I create a Dart package for
<a-form-input>
. In a new git repository, I add a simple Dart Pub pubspec.yaml
:name: a-form-input dependencies: polymer: anyThen, I copy in
a_form_element.dart
from my initial spike in some of the book's exploratory code into lib/a_form_input.dart
. For the most part, the JavaScript version of this library looks identical to the Dart solution—aside from the code annotations:@CustomTag('a-form-input') class AFormInput extends PolymerElement { @PublishedProperty(reflect: true) String name; @PublishedProperty(reflect: true) String value; // ... }The same class in JavaScript is the more compact:
Polymer('a-form-input', { publish: { name: {reflect: true}, value: {reflect: true} }, // ... });I am going to be honest here: I love the Polymer.dart annotations. If you would have told this old dynamic language aficionado a year ago that I'd love annotations, I'd have thought you were crazy. But now I really appreciate how they distinguish meta-information in the class from actual code. These published properties are core to the functionality of
<a-form-input>
, but in JavaScript that information gets lost in the rest of the code:Polymer('a-form-input', { publish: { name: {value: 0, reflect: true}, value: {value: 0, reflect: true} }, attached: function(){ this.lightInput = document.createElement('input'); this.lightInput.type = 'hidden'; this.lightInput.name = this.getAttribute('name'); this.parentElement.appendChild(this.lightInput); }, nameChanged: function(){ this.lightInput.name = this.name; }, valueChanged: function(){ this.lightInput.value = this.value; } });Still, the task at hand tonight is not language comparing. Instead, I want to change the test element in Dart from extending the base. So. in the smoke test page for the book example, I add the
is
attribute:<!DOCTYPE html> <html lang="en"> <head> <link rel="import" href="packages/form_example/elements/x-pizza.html"> <link rel="import" href="packages/a-form-input/a-form-input.html"> <script type="application/dart">export 'package:polymer/init.dart';</script> </head> <body> <form action="test" method="post"> <h1>Plain-old Form</h1> <input type=text name="plain_old_param" placeholder="Plain-old form field"> <x-pizza name="pizza_toppings" is="a-form-input"></x-pizza> <button>Order my pizza!</button> </form> </body> </html>Which almost works… except not at all (some of the functionality works, but ultimately crashes ugly).
I finally read the actual documentation to find that this is not how "is" works at all! It is meant to extends native DOM elements, not arbitrary elements like I am trying to do here. Sometimes there is nothing for it, but to admit that I am an idiot. OK, much times.
But while working this, I ran into an interesting problem. My
AFormInput
class does not seem to see attributes changing when moved into a package. That is, the same exact a_form_input.dart
when used locally or used via a package… behaves differently. And it is an important difference. The attributeChanged()
callback is never invoked in the super class via a package:library a_form_input; import 'package:polymer/polymer.dart'; import 'dart:html'; @CustomTag('a-form-input') class AFormInput extends PolymerElement { @PublishedProperty(reflect: true) String name; @PublishedProperty(reflect: true) String value; // ... AFormInput.created(): super.created(); void attached() { print('attached!'); super.attached(); } void attributeChanged(String name, String oldValue, String newValue) { print('$name: $oldValue → $newValue'); if (name == 'name') lightInput.name = newValue; if (name == 'value') lightInput.value = newValue; } }If my test element (the old
<x-pizza>
standby) uses a local copy of this same file, then it can update value
:import 'package:polymer/polymer.dart'; import 'a_form_input.dart'; // import 'package:a-form-input/a_form_input.dart'; @CustomTag('x-pizza') class XPizza extends AFormInput { // ... _updateText() { value = 'First Half: ${model.firstHalfToppings}\n' 'Second Half: ${model.secondHalfToppings}\n' 'Whole: ${model.wholeToppings}'; } // ... }But if I swap the
a_form_input.dart
lines, then changes to the value attribute (or the name attribute) never trigger the attributeChanged()
callback. I play with this for some time, but I cannot solve this. Everything else works—a hidden input is generated by <a-form-input>
in either case. The attached()
lifecycle method is called in either case. But I am clearly missing something in the packaged version. Something to solve tomorrow.Day #13
No comments:
Post a Comment