Creating Cool Components With Polymer.Templatizer
I’ve been looking for a way to create a collection component, in which I would declare the collection item template in the Light DOM and then render that template bound to each item. I’m using Polymer, because it is fun and insanely productive tool for creating Web Components. When I was already close to actually writing my component in pure Web Component API I discovered the Templatizer bevahiour.
I am working with JSON-LD and Hydra. Nicely compacted, a Hydra collection is a JSON object with
an array of members
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
A simple way to display such collection would be to create a <template is="dom-repeat">
to iterate over the members.
However this isn’t perfect and I wanted a better component, where the item’s template is supplied in the Light DOM.
This way it can be reused for rendering various collections and also extended with <content>
tags to support stuff
like custom header/footer or paging controls (see PagedCollection). For the time being, I call it
simply <hydra-collection>
.
TL;DR; here’s the working solution
First attempt (¡doesn’t work!)
My naive attempt was to distribute an item template inside a repeater.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Unfortunately this cannot work, because it is not how <content>
tags are used. Basically, the first time
the repeating template iterates, all nodes matched by <content>
are distributed and subsequent iterations
render nothing. The other problem is with data binding, which is done in the parent scope. Obviously I need a
template in the Light DOM.
<template>
in Light DOM (¡almost works!)
Inspired by a google group post by Eric Bidelman I thought that I could define a template in the Light DOM and then clone and bind it inside my component. This will first solve the binding problem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
This time I tried instantiate the templates by using native Web Components API. Unfortunately
the template cloned from a node distributed by Polymer contains only an empty #document-fragment
. It may be a problem
with Shady DOM so other Polymer quirk (it definitely works with pure-WC with Shadow DOM). Also the native
way doesn’t help with data binding anyway.
Nevertheless Polymer’s dom-repeat
component does a similar thing and looking at the source code I discovered
Polymer.Templatizer.
Polymer.Templatzier
Polymer.Templatizer is the Polymer way to create instances of templates and takes care of data binding too. It can be
added to any Polymer element as a behaviour and adds a number of methods, out of which the most
important are templatize
and stamp
, which prepare the template and create actual instance respectively.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
This works like charm but I find two minor issues with this API. First, it is weird that templatize
doesn’t return
a value, but rather modifies some internal state used by stamp
. I would prefer that to be more functional:
1 2 3 |
|
Second is a problem with the stamp method. The documentation says it accepts an object with the initial state to bind to but that didn’t work for me. It is merely a nuisance though, because any property set on the stamped clone and bound just fine. So, that’s why instead of
1 2 3 |
|
I had to write
1 2 |
|
I’m not sure why I had this problem though, because it sure as hell works in plunker.
Bottom line
I very much like the Templatizer . It makes it possible to create very rich and composable web components while still giving the developers full power of Polymer magic!