<x-flickr> - custom Polymer element

Let me state something at the very beginning: Polymer is awesome. But what is exactly Polymer?

Polymer is a new type of library for the web, built on top of Web Components, and designed to leverage the evolving web platform on modern browsers.

In other words, it is an extremely powerful library to build your own, reusable widgets for the web. Web Components include a set of great standards such as the Shadow DOM, templates and of course, custom elements.

There are a lot of great resources out there that speak about these standards - my recommendation is that if you're interested in this topic you go ahead and read all the relevant material on html5rocks.com.

For the purposes of this article I am going to be discussing the Shadow DOM and Custom Elements.

My bet is that you've seen the Shadow DOM in action before but you have not realised it. So what is it exaclty? The Shadow DOM is nothing more than an extension of an already existing element in the DOM. A typical example would be a video player written in HTML - the play, pause and stop buttons as well as the volume slider are likely to be Shadow DOM elements that belong to the root video player element. One great way to see if a particular element is a Shadow DOM or not is to use Chrome DevTools for example - however, first, make sure that you enable Shadow DOM inspection. To achieve this open your DevTools Settings panel by clicking on the cog and under General settings find the checkbox that reads 'Show Shadow DOM' - make sure that it's checked.

The following example is taken from W3Schools and you can try this out as well. Examine the audio player carefully using Chrome DevTools and you will see that the <audio controls> HTML element contains something called a #document-fragment.

As you can see from the example above every button, slider and control that is available for the audio player does in fact come from the Shadow DOM.

Why is this interesting you may ask? Well, the most important thing to remember is that the actual content is separated from the presentation - i.e. it is encapsulated. This allows you to project your content into predefined places - change how this projection is done at once, centralised location as opposed to in various locations throughout multiple files. Imagine you'd like to add another control for the audio player - you'd have to modify your code once - and whenever you reuse the <audio> tag your changes will automatically be picked up. In a more technical way - the Shadow Host (the main element) will contain the Shadow Root - which is the root of the DOM subtree containing the Shadow DOM nodes. Here's an excellent Shadow DOM Visualiser tool that should help you understand the Shadow Host/Root relationship.

The second thing that needs to be discussed is the Custom Element. The example that HTML5Rocks brings up is the source code of GMail - even though it is considered as a modern web application it lacks expression. It's source code just shows divs after divs after divs. Custom Components were created to give meaning to elements and web applications as well as to help them being maintainable. Custom Elements allow you to create your own HTML/DOM elements as well as to boundle presentation and functionality into a single tag.

So, all this introduction should be enough but I'd like to share one more piece of information - Polymer builds on top of the Shadow DOM and Custom Elements and gives us a great library to work with and utilise the best of these technologies. Here's a great diagram courtesy of the Polymer Dev Team:

The idea behind the libray is to provide you with polyfills (red bricks) that are eventually going to be removed as more and more browsers implement these new features.

I also know that while reading this article you will ask the question - What is the difference between an AngularJS directive and a Polymer element? Good news - someone from Google has already answered this question.

So enough being said - let's get down to business.

The Custom Polymer element that I've created has been inspired by Addy Osmani's x-instagram.

To see this demo in action please register for a Flickr API key. Once that's done remember your key as we'll need it after.

To create a custom element we have to do two things. First, create the element defintion - this will be done in a single .html file and we'll import that into our main file (for the purposes of this artile this file is going to be called index.html).

In light of the above the content of index.html should be:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Custom flickr element using Polymer</title>
    <script src="components/platform/platform.js"></script>
    <link rel="import" href="src/x-flickr.html">
</head>
<body>
</body>
</html>

Let's also create the Custom Element - x-flickr.html - to do this we need to first make sure that we import the right modules - as we are going to make a REST API call we need Polymer-JSONP:

<link rel="import" href="../components/polymer-jsonp/polymer-jsonp.html">
<polymer-element name="x-flickr" attributes="apikey tag amount">

Here we are defining the attributes that our Custom Element can later on use.

After this we can setup a template block - this is the bit where you can add styling to the Custom Element. I'll get back to this bit in a moment.

Now we have only created a single element that does nothing - we need to go out to the Flickr API service and collect data. To do this we need to add the following element:

<polymer-jsonp id="ajax" auto url="http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key={{apikey}}&tags={{tag}}&per_page={{amount}}&page=1&format=json&jsoncallback="></polymer-jsonp>

(Note that we are adding some bindings here - we will get that as well soon).

Now it's time that we script our Custom Element and tell it what to do with this data. In order to achieve this we need to add the following piece of code inside a <script> tag:

Polymer('x-flickr', {
    amount: 10,
    photos: [],
    apikey: '[your-API-key]',
    ready: function () {
        this.$.photos.model = this.photos;
        this.$.ajax.addEventListener('polymer-response',
                function (e) {
                    this.photos = {photos: e.detail.response.photos.photo};
                    this.$.photos.model = {photos: e.detail.response.photos.photo};
                    this.fire('x-flickr-load', {response: e.detail.response.photos.photo});
                }.bind(this)
        );
    }
});

(Note: It's a good idea to add a few console.log() statements into the event listener section to see what data is being returned by the AJAX call)

Here we create a photo model - a data model on a template and bind it back to the element. This means, that inside our <template> tag we can use the photos object as a variable:

<div class="container">
    <p>Photos of <strong>{{tag}}</strong> -- showing {{amount}} results</p>
    <template  id="photos" repeat="{{photo in photos}}">
        <div class="thumbnail">
            <img src="http://farm{{photo.farm}}.staticflickr.com/{{photo.server}}/{{photo.id}}_{{photo.secret}}.jpg" class="img-thumbnail">
        </div>
    </template>
</div>

And this would be all. Now you can use the Custom Element by adding the <x-flickr> tag to your HTML page (along with the necessary Polymer libraries). You have access to the following options:

  • apikey: your API key can be specified as an attribute as well - e.g. <x-flickr apikey="yourkey"></x-flickr>
  • tag: tag to search for - e.g. <x-flickr tag="rome"></x-flickr>
  • amount: number of photos to be returned - e.g. <x-flickr amount=15></x-flickr>

As always - the source code is available on GitHub along with the installation instructions.

Finally here are a few screenshots of the Custom Element in action:

<x-flickr tag="rome" amount="4"></x-flickr>

Happy Polymering!

Show Comments