Polymer Templates - Part 2 - Logic

We keep learning polymer’s templates features. Its time to take a look at iterating templates, conditional flow and referencing another template. If you would like to review bindings to a property, a complex object or a specific context please review Polymer Templates - Bindings

By now you got the idea and differences between TemplateBinding and Polymer. From this point onwards I will just add the Polymer’s version.

knowledge revision: in our previous article we talked about Bindings. The native template element does not provide bindings capabilities. This is one if those the enhanced features provided by TemplateBinding library created by the Polymer team. This library is used by Polymer under the hood but it could be used as standalone.

Repeating Templates

Repeat in Polymer has two possible formats: tacit or named scope. The tacit form looks like this:

<polymer-element name="my-element">
  <template>
    The brother names are
    <template repeat="{{ brothers }}">
      <span>{{ name }}</span>
    </template>
  </template>
  <script>
    Polymer({
      created: function(){
        this.brothers = [
          {name: 'Santiago'},
          {name: 'Pablo'},
          {name: 'Veronica'}
        ]
      }
    })
  </script>
</polymer-element>

The named scope form is:

<polymer-element name="my-element">
  <template>
    <template repeat="{{ brother in brothers }}">
          <span>{{ brother.name }}</span>
    <template>
  </template>
  <script>
    Polymer({
      created: function(){
        this.brothers = [
          {name: 'Santiago'},
          {name: 'Pablo'},
          {name: 'Veronica'}
        ]
      }
    });
  </script>
</polymer-element>

Sometimes you need the the iteration Index. You can use the iteration index like any other variable using double mustache syntax:

<template repeat="{{fruit, i in fruits}}">
  <div>{{i + 1}}. {{fruit}}</div>
</template>

Note: A few features of native templates can’t be replicated perfectly with the polyfills library, and require some workarounds. Some browsers don’t allow ‘template’ elements inside certain elements like ‘select’ or ‘table’. Browsers with native support for ‘template’ allow it to be a child of elements ‘select’ and ‘table’.

<table>
  <template repeat="{{fruit in fruits}">
    <tr><td>{{fruit}}</td></tr>
  </template>
</table>

Here is the workaround

<tr template repeat="{{fruit in fruits}}">
    <td>{{fruit}}</td>
</tr>
<option template repeat="{{fruit in fruits}}">{{fruit}}</option>

Conditional Flow

You can use ‘bind if’ or ‘if’:

<template bind if="">
  Binds if and only if conditionalValue is truthy.
</template>

<template if="">
  Binds if and only if conditionalValue is truthy. (same as *bind if*)
</template>

Polymer has no else clause. Use a negative condition instead:

<template if="{{!showAnswer}}">
  ...
</template>

Here is how it looks in a polymer element:

<polymer-element name="my-element">
  <template>
    <template if="{{showAnswer}}">
      <div>42</div>
      <button on-tap="{{toggleView}}">Show question</button>
    </template>
    <template if="{{!showAnswer}}">
      <div>
        What is the answer to "The Ultimate Question of Life, the
        Universe, and Everything"?
      </div>
      <button on-tap="{{toggleView}}">Show answer</button>
    </template>
  </template>
  <script>
    Polymer({
      showAnswer: false,
      toggleView: function(e, detail, sender) {
        this.showAnswer = !this.showAnswer;
      }
    });
  </script>
</polymer-element>

Conditional repeats

You may also use to condition whether to repeat a template or not.

<template repeat if="{{ conditionalValue }}">
  Repeat if and only if conditionalValue is truthy.
</template>

Conditional boolean attributes

This is another form of condition using ?= syntax. Let’s see an example:

You can set an element’s hidden property using hidden?=:

<p hidden?="{{shortView}}">
  ...
</p>

The boolean attribute gets set if it is bound to a true value. Note the use of ?= syntax for conditionally setting a boolean attribute.

<polymer-element name="my-element">
  <template>
   <div>The Big Lebowski</div>
    <p hidden?="{{shortView}}">
      'Dude' Lebowski, mistaken for a millionaire Lebowski, seeks restitution
      for his ruined rug and enlists his bowling buddies to help get it.
    </p>
    <button on-tap="{{toggleView}}">Toggle View</button>
  </template>
  <script>
    Polymer({
      shortView: true,
      toggleView: function() {
        this.shortView = !this.shortView;
      }
    });
  </script>
</polymer-element>

Referencing another template

Lets say we want to use the same template multiple times. When creating an instance, the content of this template will be ignored, and the content of #myTemplate is used instead. In the following example, the text ‘Used by any template which refers to this one by the ref attribute’ will be printed twice.

<polymer-element name="my-element">
  <template>
    <template id="user">
      <span style="userName">{{ name }}</span>
    </template>

    Hi, <template ref="user" bind="{{loggedIn}}"></template>.
    People you may want to add:
    <ul>
      <!-- tacit binding of each object inside collection-->
      <template repeat="{{peopleYouMayKnow}}">
        <li><template ref="user" bind></template></li>
      </template>
      <!-- named scope binding of each object inside collection-->
      <template repeat="{{person in peopleYouMayKnow}}">
        <li><template ref="user" bind="{{person}}"></template></li>
      </template>
    </ul>
  </template>
  <script>
    Polymer({
      created: function(){
        this.loggedIn = { name: 'Sam' };
        this.peopleYouMayKnow = [{ name: 'Amy' }, { name: 'Lin' }, { name: 'Peter' }];
      }
    });
  </script>
</polymer-element>

Recursive Templates

You can also use it to easily represent tree structures with a recursive template:

<template>
  <template>
    <ul>
    <template repeat="{{items}}" id="t">
      <li>{{name}}
      <ul>
        <template ref="t" repeat="{{children}}"></template>
      </ul>
    </li>
  </template>
</template>

Choose templates dynamically

This is simplistic example on how you could dynamically decide which template to display based on a given logic.

We give each template a unique id but instead of reference one or the other, we bind the selection to our model.

<polymer-element name="my-element">
  <template >
    <template id="one">
      The username is {{username}}
    </template>
    <template id="two">
      The name is {{name}}
    </template>
    <template bind="{{user}}" ref="{{templateName}}"></template>
	</template>
  <script>
    Polymer({
      created: function(){
        this.user = { name: 'Amy'};
      },
      domReady: function(){
       this.templateName = 'two';
       if(this.user.hasOwnProperty('username')){
         this.templateName = 'one';
       }
     }
    });
  </script>
</polymer-element>

Created vs Attached vs DomReady: Polymer shortens web components lifecycle callbacks. Created - from createdCallback - is called when an instance of the element was created. Here we can instantiate our Model.

In this example, I wanted to choose the templates dynamically based on an existing model. So I decided to perform my logic in a later stage. I could have picked ‘attached’ - from attachedCallback - which is called when an instance of the element was inserted into the DOM.

I chose ‘domReady’ which is called when the element’s initial set of children exist. From the docs: “This is an appropriate time to poke at the element’s parent or light DOM children. Another use is when you have sibling custom elements (e.g. they’re .innerHTML‘d together, at the same time). Before element A can use B’s API/properties, element B needs to be upgraded. The domReady callback ensures both elements exist.”

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 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