Polymer Templates - Part 1 - Bindings

“Polymer’s TemplateBinding library extends the capabilities of the HTML Template Element by enabling it to create, manage, and remove instances of content bound to data defined in JavaScript. Although internal in Polymer, it is also useful standalone.”

In our previous article we presented a new challenge Will Polymer kill Angular 2.0?. We also said that in order to choose one over any other, we needed to know its strength and weakness and finally we came up with a list of small challenges to cover. Templates are the first one for no particular reason and if you want to read the whole list, checkout our previous post.

About HTML template element

  1. Its content is effectively inert until activated. Essentially, your markup is hidden DOM and does not render.

  2. Any content within a template won’t have side effects. Script doesn’t run, images don’t load, audio doesn’t play,…until the template is used.

  3. Content is considered not to be in the document. Using document.getElementById() or querySelector() in the main page won’t return child nodes of a template.

About Polymer templates

So Polymer team created a standalone small library called ‘TemplateBinding’. This lib stands on HTML Template Element spec’s shoulders providing binding, interpolation and some logic helpers. Lets take a look at these added features.

We’ll start with ‘bindings’. In this article we will try to gather facts, problems workarounds and tips I’ve found on from polymer’s docs, articles about html templates spec and different code repos.

Next one will be on some logic features such as repeat and conditions. Then we will go into a third article on binding on native html elements, multiple insertion points and one way binding.

Binding to a property

Standalone TemplateBinding Version

<template id="text">
  <p>My favorite color is {{color}}.</p>
</template>

<script>
  document.addEventListener('DOMContentLoaded', function() {
    var myTemplate = document.getElementById('text');
    myTemplate.model = {
      color: 'red'
    };
    // Needed to detect model changes if Object.observe
    // is not available in the JS VM.
    Platform.performMicrotaskCheckpoint();
  });
</script>

Note: TemplateBinding depends on NodeBind which depends on observe-js. TemplateBinding.js uses Platform.performMicrotaskCheckpoint() which is defined in observe-js

Polymer Version

<polymer-element name="my-element">
  <template>
    <p>My favorite color is {{color}}.</p>
  </template>
  <script>
    Polymer({
      color: 'red'
    });
  </script>
</polymer-element>

Note: A few features of native templates can’t be replicated perfectly with the polyfill library, and require some workarounds. Binding to certain attributes (such as the img tag’s src attribute) doesn’t work correctly on some browsers that don’t support templates.

For example, running

<img src="/users/{{id}}.jpg">

under the polyfill produces a network request that 404s. In addition, browsers such as IE sanitize certain attributes, disallowing {{}} replacements in their text.

To avoid these side effects, bindings in certain attributes can be prefixed with “_”:

<img _src="/users/{{id}}.jpg">
<div _style="color: ">
<a _href="{{url}}">Link</a>
<input type="number" _value="">

Binding to a complex object

Standalone TemplateBinding Version

<template id="text">
  <p>My favorite color is {{options.color}}.</p>
</template>

<script>
  document.addEventListener('DOMContentLoaded', function() {
    var myTemplate = document.getElementById('text');
    myTemplate.model = {
      options: {
        color: 'red'
      }
    };
    Platform.performMicrotaskCheckpoint();
  });
</script>

Polymer Version

<polymer-element name="my-element">
  <template>
    <p>My favorite color is {{options.color}}.</p>
  </template>
  <script>
    Polymer({
      created: function(){
        this.options = {
          color: 'red'
        }
      }
    });
  </script>
</polymer-element>

Note: For property values that are objects or arrays, you should set the default value in the created callback instead. This ensures that a separate object is created for each instance of the element!

Binding to a specific context

In order to simplify use of deeply nested objects in a template, we can use “bind” to pick our implicit context. Bindings inside the template are evaluated in the context of the bound object.

Standalone TemplateBinding Version

<template id="text" bind="{{ options }}">
  <p>My favorite color is {{color}}.</p>
</template>

<script>
  document.addEventListener('DOMContentLoaded', function() {
    var myTemplate = document.getElementById('text');
    myTemplate.model = {
      options: {
        color: 'red'
      }
    };
    Platform.performMicrotaskCheckpoint();
  });
</script>

Polymer Version

<polymer-element name="my-element">
  <template>
    <template bind="{{ options }}">
      <p>My favorite color is {{color}}.</p>
    <template>
  </template>
  <script>
    Polymer({
      created: function(){
        this.options = {
          color: 'red'
        }
      }
    });
  </script>
</polymer-element>

Note: In order for the context binding to work on Polymer I had to nest a template inside the element’s template. You will notice this pattern as we use more TemplateBinding features inside Polymer.

You can also create a named scope. This comes useful if you have nested templates.

<polymer-element name="my-element">
  <template>
    <template bind="{{ options as o }}">
      <p>My favorite color is {{o.color}}.</p>
    <template>
  </template>
  <script>
    Polymer({
      created: function(){
        this.options = {
          color: 'red'
        }
      }
    });
  </script>
</polymer-element>

Try it yourself

You should go to ele.io and take any of the Polymer examples and play!

Ele.io is the jsfiddle for polymer, created for polymer playing and developed with polymer elements.

Next article will be on logic features such as repeat and conditions. Then we will go into a third article on binding on native html elements, multiple insertion points and one way binding.

Enjoy!

comments powered by Disqus