Build a Real-Time Polymer To-Do App

If you kept up with Google I/O 2015, then you likely saw that Polymer 1.0 has been released to the public. If you have been around since Polymer 0.5, then you likely saw that all your favorite building blocks have been deprecated. For all those who have used 0.5, consider this a migration assistant. Those of you who have never seen Polymer before, you're in for a treat!

Polymer is a library that leverages web components to allow you to define and use custom HTML elements. With custom elements, designing and building a website becomes a game of Legos. Polymer 1.0 replaces the shadow DOM polyfill with a lightweight shim, reduces the code size, and speeds up data-binding for an all around smoother experience.

Today, we'll be building a realtime collaborative to-do application, where task status is synced in realtime across all connected devices as their state is changed. We'll use Polymer to build our application, and PubNub to send and receive updates between connected devices, and to sync state.

So let's get started! When building with Polymer you must import elements before you use them. A standard polymer use looks like this:

One of the best parts of Polymer, is the use of Material Design. Most of us have probably seen Material Design on Android Lollipop, but the specification for visual and interaction design spans all different screen sizes. These elements in Polymer are called paper elements, and they are made to adapt screen sizes and look beautiful on any device.

A Note on Testing and Debugging

If you try to open file://<your-polymer-project> in your browser, you will likely run into Cross-Origin Resource Sharing (CORS) errors. So, to test your code you have a few options. You can upload your files to a web server, like Github Pages if you prefer. However, to keep development local, I recommend you setup a simple server using Python.

To do this, open your terminal and change directories into your current project and depending on your version of Python, run one of the following modules.

For example, I run Python2.7 and the command I use is python -m SimpleHTTPServer 8001. Now I can go to http://localhost:8001/index.html to debug my app! Try making an index.html with anything in it and serve it on localhost before you continue.

1. Creating Your To-Do App with Paper Elements

1.1 Install Polymer

In order to use Polymer, we must first install it. The recommended method is through Bower. Follow the official instructions to install Bower. Then from the terminal, install Polymer with the following lines.

Now make your index.html and load the webcomponents-lite.js file. For those who used v0.5, bower includes all webcomponents script files, however, the new components are in the -lite file, so choose that one.

1.2 Downloading Paper Elements

First, you should know that there are loads of great looking paper elements, so if you decide to choose different elements, I fully support you. You can see all elements in the Polymer Element Catalog. Version 0.5 users, note that all core-elements are deprecated. Now the core building blocks are part of iron-elements.

As a general syntax, if you see something you like in the catalog, downloading the element with Bower looks as follows:

$ bower install polymerelements/<element-name>

The elements that I use in this demo are:

paper-checkbox to check off a task

iron-input to handle input binding functionality

paper-input for a Material Design input

paper-drawer-panel to provide the app structure (in place of the core-scaffold of v0.5)

Add the <meta name="viewport" content="width=device-width, initial-scale=1"> to your <head> tag to properly display your app on mobile devices. From there you can see the webcomponents file and a custom element that defines our app. <body fullbleed unresolved> tells the Polymer that the body should take up the full screen, and wait until all elements are resolved (loaded) before displaying anything, preventing FOUC.

2.1 Structure of Custom Elements

In this tutorial, we'll be focusing on the functionality of Polymer 1.0, not the basic CSS. However, I've included all my stylesheets, which can be found here. Otherwise, the styling is up to you.

This is the basic layout of any custom element. <dom-module> is the Polymer 1.0 version of the now-deprecated <polymer-element>, it is the root of your custom element. An element's styles are local, so add them between the style tags, and between the <template> tags are where the bulk of our element will go. The Polymer({}) script is where you write the logic to your element. is is how you register your element as a Polymer element. Any attribute of your element should be declared in properties, we will cover this shortly though.

2.2 Importing Elements

As I said earlier, you must import an element before using it. Create a new folder and file in your project directory /elements/todo-element.html. Then, in todo-element.html add the standard template from above, and import the following elements:

Time for a crash course in data binding for those who have never used Polymer before. Any time {{}} is used, it is binding a property. Whatever value that property has will be placed where the curly braces are. Before we move on, note the elevation attribute of the paper-material. This will generate a shadow to give perspective to your app. Now, lets define those properties.

Here you can see how a property is declared. Name the property, state its type and default value. Now for binding, <paper-item hidden="{{editing}}" id="task">{{task}}</paper-item> will be hidden the moment we modify the property editing to be true. This happens once doEdit is called. Note that properties in polymer native on-* syntax, such as on-tap="doEdit" do not require curly braces, and any plain text such as <span>{{user}}</span> needs to have a tag directly around it in order to render. Bindings are two-way. This means that if I modify the <paper-input value="{{task}}"> and change the value, task's value will be updated accordingly.

Lastly, take note of the completed property. there is an observer, or a function that will be called once the value of completed is changed. To communicate with other elements, this code just fires a complete event with our completed task as the tag. This will be important later. If you would like to learn more about properties and property binding, see the official documentation.

Our properties include:

user: The person who posted or completed a task

task: The task that is being posted

rid: A random ID that will be used to coordinate tasks via PubNub

time: The time of creation of the task

completed: Whether or not the task is complete

editing: If the user is editing the task

Now, we have a complete To-Do element. Time to use it to make our application.

3. Creating Your App

Our index.html only had one element, the <todo-app>, so now let's make that element! Create a file in /elements/todo-app.html.

3.1 App Imports

We can import our custom element just like any other, using <link href="../elements/todo-element.html" rel="import">. Now we should import all elements we are going to use in our app, and set it up as a Polymer element.

This snippet covers a lot of Polymer's new features so follow carefully. Templates are used internally to add functional properties such as loops and conditionals to your elements. <template is="dom-if" if="{{!done.length}}"> means that it is functioning as an if statement, on the condition of the array done's length. If done is empty, this reads not 0 which evaluates to true, and thus it will only display when the array is empty, and disappear as soon as the array is populated.

Looping Templates

<template id="done" is="dom-repeat" items="{{done}}"> is an effective way to loop through and display an array's contents. The dom-repeat means it will loop through every element of items (the done array in this case), much like a for-each loop. It names each element of the array item so if item is {task:"SIT", user:"lazy-cat"}, you can access properties as {{item.task}} and {{item.user}}. There is an option to set as="myTask" in the template tag to name elements, and then you can use {{myTask.task}};

3.4 Main Body Content

Now onto the Main Body of the Element. This can be styled or structured however you would like, but I tried using as many paper-elements as possible since my eye for style is blind.

This looks somewhat similar to the side menu. There are still conditional and repeating templates to display all tasks in the todo array. There is a function that we will need to define called postTask that is triggered on-tap of the add action button. The paper-input element uses a char-counter to display number of letters typed. See more custom properties of this element in the element catalog. Again, to view all possible icon choices for the <paper-fab>look here.

This is the first we have see how to access the value of something in your element's Shadow DOM. After declaring our task and done arrays, we get the value of our <paper-input id="tTask"> using var tsk = this.$.tTask.value;. Pretty simple! We will publish our task with PubNub in part 4, but for now take a look at the ready function. This is called once our element is loaded and ready to be rendered. We add an event listener for the completed which we fired from our custom todo-element. We handle it by updating its fields for who completed the task, and we will then publish to all collaborators.

4. Making It All Collaborative

Welcome to my favorite part, the real-time collaboration! In order to start streaming tasks and completions, you will need a publish and subscribe key. To get your pub/sub keys, you’ll first need to sign up for a PubNub account. Once you sign up, you can find your unique PubNub keys in the PubNub Developer Dashboard. The free Sandbox tier should give you all the bandwidth you need to build and test your Collaborative To-Do Application.

4.1 The PubNub Element

PubNub has a Polymer element pre-made. The <pubnub-element>. Once you have your keys the <pubnub-element> can be downloaded using Bower.

$ bower install --save pubnub-element

To import this element in your todo-app.html, using the following line:

<core-pubnub-publish> is used to send messages to all subscribers of a channel. We set the channel to "todo", so all subscribers of todo will receive tasks. Then, to publish a message, we need to access the <core-pubnub-publish> as follows.

The <core-pubnub-subscribe> will subscribe us to channel todo so we are receiving all those publishes, but notice that it takes two values of messages and presence. These are arrays. When the PubNub Element receives a message or presence update, it will put it in the array and fire a message or presence event respectively. It also takes a on-callback which is triggered any time a subscribe event occurs. We can implement that like so:

This will check if a new todo is being broadcasted, or an old one is being completed. Polymer uses its own methods to update arrays. Array.pop() will not notify Polymer that a change has occurred, so the elements you see won't be updated. Therefore it uses this.pop('arrayName') (also push, shift, unshift, etc). So we use this.unshift('todo', ourJSON); to add a task to the beginning of the todo array, which is then updated from our dom-repeat in Step 3.

4.3 BONUS: Real-Time Presence

Go to your PubNub Developer Dashboard and enable the free 30-day trial of Presence for this part, trust me it's worth it. We now have subscribe and publish working perfectly! Anywhere in your paper-toolbar, (I chose before the Main Body Title), add an icon and a number to indicate how many people you are collaborating with.