Polymer Plunge

Well it’s about time that I took the┬áPolymer Plunge. I have a project in the office that is constantly growing, frequently getting new feature or component requests. This was totally anticipated from the beginning however. As such from the get-go I decided to go with Web Components and web_ui to build the application. It made each component much more modular and easy to assemble. I’ll get more into why web_ui / dart in a different post.

But the days of web_ui are waning. Polymer is the new shining light and as its feature set starts to match web_ui the time has come for me to start migrating over to Polymer.dart. But where to start? The documentation for Polymer is still pretty sparse, and the quick pace that Polymer is evolving, the few existing blog posts and articles are becoming out-dated quickly. +Seth Ladd has started a (somewhat redundantly named) GitHub Repo: dart-polymer-dart-examples which is essentially the current de facto repository of simple Polymer examples. So I’ll start there. [Edit: Recently the documentation situation has improved significantly and a great platform for getting documentation is the dartlang.org’s Polymer.dart page. Also see the Polymer Intro Tutorial.]

First stop is the README file, as all of his notes on how to use Polymer and various quirks are stored in there. I’ll note some other issues I ran into porting over my web_ui application below. The README is fairly up-to-date and contains many of the latest and greatest changes that have occurred with the Dart Polymer port. After reading that through, be sure to go through many of the samples found in that repository’s web directory. There is no real order set to them, but it’s great for a quick and dirty cookbook type usage. Find the example you want to learn about and look at that code. One caveat I will mention is that if you’re looking for a CustomEvent example, look at the todo_element sample.

Additionally, I spent a fair bit of time looking at the tests for Polymer from the package’s repository to get a good feel for how things are expected to behave.

Now for some of the issues I ran into during my porting process:

Use <polymer-element> and not <element>
First and foremost I’ll get this simple one out of the way. When declaring a custom element in Polymer, use the <polymer-element> tag to define your element, not the old <element> tags.
dart:json is depreciated
dart:json is going away. I needed to take a little time to convert everything over to use dart:convert. Fortunately it involved very little changes, as I always prefixed my dart:json import anyways. Code changed from Json.stringify(...) over to be JSON.encode(...) and from Json.parse(...) to be JSON.decode(...).
Can’t just call query()
Because web_ui was never really integrated with the ShadowDom, it was possible to just call query() within an element’s class and you could get the nodes from within that element. Now if you want to properly access those components you have to call shadowRoot.query(). shadowRoot will return the ShadowDom for this element, and is the same as calling getShadowRoot('my-tag'). You will sometimes see the latter in sample code as early on there were some issues with shadowRoot returning null when it shouldn’t be. These have since been resolved.
ObservableMixin is already included
Sticking with some legacy Polymer stuff, many Polymer samples will show that you need to define the element’s class as class MyElement extends PolymerElement with ObservableMixin. Fortunately, because it was such a common situation, ObservableMixin is automatically included for us now. We no longer need to add the with ObservableMixin portion every time.
@CustomTag() Annotation replaces constructor attribute
In web_ui if we had a class with the UpperCamelCase name of our custom element, it would automatically perform the association of element to class for us. If we wanted to specify a different class to use for the custom element, we could pass the constructor attribute specifying what class to use for the element’s model. That functionality doesn’t exist with Polymer, and instead we must use the @CustomTag() annotation and pass in a string which matches the value passed to the name attribute of our Polymer Element. In the long run this makes it much easier to choose our associated classes and identify them in the code without the need to flip back and forth to the HTML file if we decide on a classname change later.
No dash-to-camelCase Mapping
One feature of web_ui that I admit I miss is the automatic mapping of dashed attribute-names to camelCase attributeNames. For instance if I had my-value in web_ui it would automatically map it to myValue in the dart code. That functionality has been removed to keep similar behaviour with the JavaScript version of Polymer. The current recommended method is to use camelCase or lowercase attribute names in the HTML and camelCase in the dart code (HTML is case insensitive so Polymer is maintaining that).
Make your entire app a tag
In Polymer the suggested style is to make the entire app a tag. Essentially your index page would look something like: ...<body><my-app></my-app></body>. In part this is because in Polymer, everything is a tag. However this is actually important as well because unlike web_ui, you cannot bind variables (single or two-way) which are not within template tags. Additionally, you can’t bind global variables, and rather bind variables from the associate model directly.
Bind to value= and not with bind-value
In web_ui if we wanted to do two-way binding, we would use bind-value or maybe bind-value-as-number or something like that. Fortunately that’s been simplified in Polymer. Now we bind directly as you would do for one-way binding, but we pass it as the value= for the input element. That is now we would write: <input type="text" value="{{myValue}}"> instead of using <input type="text" bind-value="myValue">. Note that the addition of the moustache braces {{ }}} is required.
Need to manually bind observable getters
It used to be very simple to implement observable getters in web_ui. As long as you added the @observable annotation to the getter, and as long as one of the backing properties it used was observable, then you had an easy observable getter. In Polymer it’s a bit more involved. In order to setup an observable getter we need to setup an onPropertyChange binding for our backing observable, and then from the callback that takes, we need to call notifyProperty on the getter. (Note: on the plus side, now that we have support for Symbol literals, with the # sign, this becomes much shorter) onPropertyChange(this, #backingObservable, () => notifyProperty(this, #obserableGetter)); where #backObserable is the name of the observable that’s used, and #observableGetter is the name of the getter that we want to be observable.
Apparently, I’m just learning, if you have a variable named foo and setup a method named fooChanged(var oldValue) (or accepts whatever type of foo is) then it will automatically create the onPropertyChange stuff for you and instead just execute whatever is in your method. So my above code could be: backObservableChange(var oldValue) => notifyProperty(this, #observableGetter); That’ll help keep things even that much cleaner for you. And no worries about where to put the property bindings.
Public variables are not automatically attributes
In web_ui anytime I wanted to make an attribute on the Custom Element’s HTML tag, I just had to make sure the value was a public property in the corresponding class. Polymer is a little more secretive. Initially polymer required an attributes tag to manually specify which properties were to be settable attributes from the tag. Unfortunately this made for a lot of back and forth while changing or adding variables, putting it in the attributes tag and then in the corresponding dart class. However that was all changed with addition of the @published annotation. Now any variable that is flagged with the @published annotation automatically becomes observable and it is accessible as an attribute on your custom element’s tag.
Can’t pass arguments to event callbacks
A common pattern for me in web_ui was to pass arguments to my callbacks, such as: .. on-click="handleClick($event)" or sometimes I’d pass a string as the argument. All of that is no more though. With event callbacks in polymer, you cannot provide a full method to call, rather you only provide the name of the method. That method is then called automatically with 3 arguments. So your event binding still looks pretty similar, but you’re dropping the parens .. on-click="handleClick". You’re method should then have a signature like this: void handleClick(Event e, var details, Node node) {.... We automagically receive the Event and even the node that triggered the event.

Binding to disabled attribute
Another surprise I ran into with binding to attributes, was with the disabled property. With web_ui I could bind a boolean value to disabled with something like: .. disabled="{{isDisabled}}". However if I do that in Polymer, the element is disabled regardless of if isDisabled is true or not. Instead the solution was to use a type of conditional assignment I had not realized existed: .. disabled?="{{isDisabled}}". Now when isDisabled is false, then the element is active and working.

I’m still in the process of migrating my code-base over to Polymer. However in most cases I’ve found it to be an almost direct copy/paste process. There have been a few changes such as adding the @published annotation, or a few other minor changes as I move from a callback system to a proper custom events system. Most of the changes I’ve needed to make were in the HTML declarative syntax for the Polymer Elements. Most of my dart classes remained virtually the same.

In general, Polymer has made creating and working with HTML fun again. To so simply declare my own elements and reuse them again and again, and to consistently achieve the same results… This is what I’ve been looking and waiting for since I was 14!

See my follow up post: Polymer Plunge: Diving In for a few more caveats of note.

This entry was posted in Dart and tagged . Bookmark the permalink.

Have Something To Add?

Loading Facebook Comments ...
Loading Disqus Comments ...