Overview
On May 29, 2015, the polymer team announced the release of Polymer 1.0. Since the release of Polymer 0.5 developer preview, the team has rewritten the library from the ground up, with increased performance and better functionality as results.
Polymer is one of the first implementations of a user interface library built upon the Web Components standard. Web Components are not fully supported by browsers, but they provide a polyfill library, webcomponents.js, that provides enough functionality to support Web Components and Polymer.
Web Components is the result of the evolution of user interface libraries over the past decade. At one point, we strove to separate our HTML, CSS, and JavaScript and ran our HTML through W3C validators. This led to unintended complexities… For example, looking at a .css file, you couldn’t easily determine which selectors are actually used in your HTML and especially programmatically used in JavaScript. Similarly, your JavaScript code was difficult to organize so that code could be reused efficiently on multiple pages.
Polymer uses the term “element” in place of “web component,” so I’ll use that term in the rest of this article as well.
Polymer 1.0 Basics
Polymer elements neatly bind together the HTML, CSS, and JavaScript for individual custom elements that are highly independent and reusable:
<!doctype html> <html> <head> <title>Figure 1</title> <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> </head> <body> <link rel="import" href="bower_components/polymer/polymer.html"> <dom-module id=“my-element”> <style> h1 { color: red; } </style> <template> <h1>My Element</h1> <content></content> <button id=“click-me” on-click="handleClick">Click Me</button> </template> <script> Polymer({ is: 'my-element', handleClick: function() { alert('clicked'); } }); </script> </dom-module> <my-element> Whatever content I want </my-element> </body> </html>
Listing 1: figure1.html
Figure 1: output of figure1.html
Note the on-click attribute of the Click-Me button. You define the handlers for events for the element in the Polymer() call. Also, the tags in the element template specifies the point where anything between and the closing tag is injected.
Applications written using Polymer have very descriptive and obvious markup:
<html> <head> … <!-- Polyfill Web Components support for older browsers --> <script src="components/webcomponentsjs/webcomponents-lite.min.js"></script> <link rel="import" href="bower_components/polymer/polymer.html"> <link rel=“import” href=“components/blog-post.html”> </head> <body> <blog-post> <blog-title>My Blog Post</blog-title> <blog-content></blog-content> <blog-comments></blog-comments> </blog-post> </body> </html>
Listing 2: Sample application markup
The example in Figure 3 includes several custom elements: “blog-post,” “blog-title,” etc. Each of those might be implemented in its own file, but for simplicity, I’ve suggested that they are all provided by a single “import”, components/blog-post.html.
HTML Imports
The link/import tag is the #include for the WWW. It is a feature of the Web Components specification. It has similar behavior to the <script> tag, but fetches and injects a snippet of HTML, or in Polymer’s case a component, inline. Where it differs from <script> tag is that if you import the same HTML file (URL) more than once, it is only injected the first time. Imports are also cached by the browser, so reuse of components via import are less a concern when considering the number of requests from the browser to the server to fetch them.
In this article, I won’t be creating separate .html files for each component/element. Everything including the <dom-module> through the </dom-module> lines in the code samples could be put in separate .html files and loaded via the HTML import mechanism. In fact, this will almost certainly be what you will do in practice.
Bower
Polymer uses the Bower Package Manager to deal with element dependencies and to provide an open ecosystem for the sharing of open sourced (and private) elements. The Polymer team provides a number of ready-to-use elements that Bower is used to install. There will be many 3rd party elements available as Polymer 1.0 gains acceptance.
Shadow DOM
Polymer uses Shadow DOM, which provides all sorts of flexibility. Two obvious benefits you’ll see when using Polymer are the lack of conflict in element IDs across many elements, and the ability to locally style a component without creating conflicts with CSS selector names in other elements or the application itself. Consider:
<!doctype html> <html> <head> <title>Figure 1</title> <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> </head> <body> <link rel="import" href="bower_components/polymer/polymer.html"> <dom-module id="custom-button1"> <style> #my-button { color: red; } </style> <template> <button id="my-button"><content></content></button> </template> <script> Polymer({ is: 'custom-button1' }); </script> </dom-module> <dom-module id="custom-button2"> <style> #my-button { color: blue; } </style> <template> <button id="my-button"><content></content></button> </template> <script> Polymer({ is: 'custom-button2' }); </script> </dom-module> <custom-button1>Red Button Text</custom-button1> <custom-button2>Blue Button Text</custom-button2> </body> </html>
Listing 3: Shadow DOM
The output of the program in listing 3:
Figure 2: demonstrating shadow DOM, output of Listing 3.
Even though the two buttons have the same id, “my-button,” the styles for custom-button1 applies to its button, and the styles for custom-button2 applies to its button.
Data Binding
One way binding and iteration is easy to figure out and use.
<!doctype html> <html> <head> <title>Figure 3</title> <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> </head> <body> <link rel="import" href="bower_components/polymer/polymer.html"> <dom-module id="parent-children"> <style> div { border: 1px solid black; padding: 5px; margin-bottom: 10px; } </style> <template> <div> <p>My name is <span>{{name}}</span></p> <p>I have <span>{{count}}</span> children:</p> <ol> <template is="dom-repeat" items="{{children}}"> <li>{{item}}</li> </template> </ol> </div> </template> <script> Polymer({ is: 'parent-children', properties: { name: { type: String, value: 'John' }, children: { type: Array, value: [ 'child 1', 'child 2', 'child 3' ] }, count: { type: Number, computed: 'getCount(children)' } }, getCount: function(children) { return children.length } }); </script> </dom-module> <parent-children></parent-children> <parent-children name="Paul"></parent-children> <parent-children name="George" children='["child a", "child b"]'></parent-children> <parent-children id="ringo" name="Ringo"></parent-children> <script> document.addEventListener('WebComponentsReady', function () { console.log('polymer-ready'); document.getElementById('ringo').children = [ 'one', 'two', 'three', 'four ' ]; }); </script> </body> </html>
Listing 4: Data binding
The output of the program in listing 4:
Figure 3: Data Binding, output of Listing 4
Two-Way Binding
Two-way binding is cooperative. You listen for events on specific properties for which you care about dealing with value changes.
<!doctype html> <html> <head> <title>Figure 1 </title> <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"> </script> </head> <body> <link rel="import" href="bower_components/polymer/polymer.html"> <dom-module id=“my-input> <style> #ti { color: red; } </style> <template> <input id="ti" type="text" value="{{tival::input}}"> <div id="val">value: </div> <button on-click="abc">abc </button> </template> <script> Polymer({ is: 'my-input', properties: { tival: { type: String, observer: 'handleChange' } }, handleChange: function() { console.log('change') this.$.val.innerHTML = 'value: '+ this.tival; }, abc: function() { this.tival = 'abc'; } }); </script> </dom-module> <my-input></my-input> </body> </html>
Listing 5: Two-Way Data Binding
The output of the program in Listing 5:
Figure 4: 2 way binding in action. 1st typing “works”, second clicked abc button.
Impressions
My experience with 1.0 is mixed. The differences between 0.5 and 1.0 are significant enough that the many good things done with 0.5 are not useful anymore. The documentation for 0.5 is much better, but not completely relevant. The 0.5 to 1.0 migration guide is a decent way to bridge the two, but the 1.0 documentation really needs to be worked on a lot to bring it up to snuff.
Pros
I really like the idea of encapsulating the functionality of a component/element in something like the dom-module syntax Polymer provides. IMO, too many frameworks force you to overly complicate your project structure by creating separate files for JavaScript, CSS, and HTML. It gets worse when these files are stored in different places in your project’s directory hierarchy.
Polymer provides responsive design out of the box and adheres to Google’s Material Design philosophy.
Right away, there are quite a few elements provided by the Polymer team to begin crafting applications.
Polymer is agnostic about its use in single page apps versus being used in multi-paged apps. My view is that there is a place for both in the world.
The learning curve for Polymer is measured in hours. Compared to heavier weight frameworks like ExtJS or Angular, you will sooner be spending your time crafting your application instead of trying to figuring out and fighting with the core of the framework to achieve what you desire.
No transpiler or watcher is needed.
jQuery isn’t required.
I’m a big fan of Bower. It is not opinionated.
Cons
The initial suite of available add-on elements is decent but not at all sufficient to create even moderately advanced applications. For example, there are no data grid elements available, let alone highly optimized ones.
Their suggestion/requirement for using bower to create your custom elements is fine once you’ve actually implemented your custom elements. During development, it is unnecessary work to have to commit a change, push to a repository, then bower update before you can test your change.
The combination of webcomponents-light.min.js and polymer.html is on the order of 128K bytes before gzip. If you’re creating a single page app, this isn’t that big a deal, but it isn’t great for multi-page sites.
The use of HTML import can generate a lot of requests to the server to fetch each element. One of the primary rules of website optimization is to reduce the number of requests. There is no bundling mechanism that I’ve run across for addressing this problem. Any solution would have to inspect third party elements within their HTML files and deal with their dependencies (and the dependencies of the dependencies).
HTML imports require a URI path and Polymer is using relative paths throughout. This really makes it difficult to refactor project layout. Most everything is going to be bower_components/whatever
Use of Mustache convention actually collides with Mustache/Handlebars/HoganJS/etc. I can think of many reasons why I’d want to process a web component/element source file with a template language like Mustache before serving it to the client/browser. Two obvious ones are to be able to comment out chunks of the file using {{! … }} syntax, and to dynamically generate the paths for HTML imports lines.
HTML comments in component/element HTML files are sent to the client. Do you really want to waste bandwidth on this? Realize that some comments you do want to send, some you don’t.
Polymer is a Google project. I can see the synergy between Polymer and Chrome, but I don’t see why they’re wasting their time with competing technologies (AngularJS and Polymer). I consider this a con because who knows if they’ll show lesser support for one or the other as time goes on.
Conclusion
If you’re excited about Web Components, which are the future of the World Wide Web, you can start using them now with Polymer. Your code will be relatively easy to port and maintain as browsers adopt and implement more and more of the ES Next and Web Components specifications. Though you currently won’t find the ultimate component to do quite a few things, Polymer is already good enough to use to make a lot of applications.
Mike Schwartz
Related Posts
-
NativeScript Layouts for HTML Ninjas
NativeScript is an awesome framework for building cross-platform applications. This article shares tips for creating…
-
NativeScript Layouts for HTML Ninjas
NativeScript is an awesome framework for building cross-platform applications. This article shares tips for creating…