Software development

Declarative web applications with Polymer’s two-way data-binding

Topics
Polymer, Web Components, declarative

Lately I’ve been having a lot of fun with Google’s Polymer, trying out different patterns of creating web apps with it. Polymer is a future-oriented frontend library based on Web Components standards. I have a strong feeling Web Components will fundamentally change the way we build the web. Polymer is just one of the libraries that try and make using them as easy as possible.

Polymer authors are quite big on declarative. They argue HTML used to be entirely declarative in the days of yore, but has failed to keep up with the times. Custom Elements are the solution to this discrepancy: just like a <select> element magically transforms into the dropdown we all know, we can now define our own Custom Elements, that can have functionality of their own.

In order to try out the declarative pattern, I set out to create a currency converter with as little JavaScript as possible, trying to leave all the heavy lifting to data-bindings alone.

This is what we’ll end up with, so hold on to your hat and enjoy the ride!

Step 1. Must-have pieces of UI: the template

Polymer uses the familiar AngularJS handlebars expression syntax for the data-binding:

{{ value | filter }}

Using this syntax, we can do something like the following to arrive at an almost-working solution.

<input value="{{ amount }}">
<input value="{{ fromCurrencyRelativeValue }}">
<input value="{{ toCurrencyRelativeValue }}">
<div class="result">
    {{ (amount * (fromCurrencyRelativeValue / toCurrencyRelativeValue)) }}"
</div>

In essence, in the result div we have a simple calculation expression: the amount entered multiplied with the fraction of the relative values.

However, there are no restrictions on what the `fromCurrencyRelativeValue` and `toCurrencyRelativeValue` can be, which would definitely be a problem.

Step 2. Make it work

There is a small amount of Polymer boilerplate that we need to get over with to make the element do its thing. It looks a little something like this:

<polymer-element name="currency-converter" attributes="amount fromCurrency toCurrency">
  <template>
    <!-- The above stuff here. -->
  </template>
  <script>
    Polymer({
      // New stuff here?
    });
  </script>
</polymer-element>

See how I added the variables as the element’s attributes? This allows us to use the element like so:

<currency-converter
  amount=100
  fromCurrency=0
  toCurrency=1>
</currency-converter>

But back to the topic. The JS part in the boilerplate above is for defining the Custom Element’s prototype. We could of course define the currencies elsewhere (as an attribute or even using an external JSON resource), but for this demo, let’s go ahead and add the data straight to the converter’s prototype.

Polymer({
  /**
   * The data model holds currencies' names and relative values.
   */
  currencies: [
    {
      name: "EUR",
      relativeValue: 1.13085
    }, {
      name: "SEK",
      relativeValue: 0.1189895
    }, {
      name: "USD",
      relativeValue: 1.0
    }, {
      name: "NOK",
      relativeValue: 0.1311897
    }, {
      name: "JPY",
      relativeValue: 0.00836764
    }
  ]
});

Okay, now we have the HTML template from Step 1, the necessary boilerplate, and the data model. It’s all good! But let’s not stop while we’re having fun!

Step 3. Usability  and swag

The text input for selecting a currency index is obviously a poor decision, but while we’re at it, why not decorate all of the stuff with some Material Design goodness?

<paper-input-decorator floatingLabel label="Amount">
  <input is="core-input" type="number" value="{{ amount }}">
</paper-input-decorator>
<!-- … -->
<paper-dropdown-menu label="From" flex>
  <paper-dropdown class="dropdown">
    <core-menu class="menu" selected="{{ fromCurrency }}">
      <template repeat="{{ c in currencies }}">
        <paper-item>{{ c.name }}</paper-item>
      </template>
    </core-menu>
  </paper-dropdown>
</paper-dropdown-menu>

Also, let’s add a swap button for quick to-fro:

<div class="swap" on-tap="{{ swap }}">
  <core-icon icon="swap-horiz"></core-icon>
</div>

Finally, show the result with a label above it:

<section class="result">
  <p class="description">
    {{ amount + ' ' + currencies[fromCurrency].name + ' in ' + currencies[toCurrency].name }}:
  </p>
  <p class="result-number">
    {{ (amount * (currencies[fromCurrency].relativeValue / currencies[toCurrency].relativeValue)) | toFixed(2) }}
  </p>
</section>

Phew, that’s it! All we need to do now is define the two functions we are now referencing: the `swap` method and the `toFixed` filter. They are very simple.

/**
 * Filter for bringing numbers to a defined precision.
 */
toFixed: function(value, precision) {
  return Number(value).toFixed(precision);
},
/**
 * Method that swaps the two selected currencies with each other.
 */
swap: function () {
  var temp = this.fromCurrency;
  this.fromCurrency = this.toCurrency;
  this.toCurrency = temp;
}

 

 

Step 4. Admire our accomplishments

The demo page over on GitHub hosts 3 separate `<currency-converter>` elements. Their invocations are described below each element like so:

All of the code is available too, the gist of it being the Custom Element file we’ve been going through here.

Demo: http://ohanhi.github.io/declarative-polymer/
Code: https://github.com/ohanhi/declarative-polymer/

Happy hacking!