Rails and Web Components Part 1: Adding Polymer to a Rails App
My horizons were expanded a little bit by Dane O'Connor’s recent talk on web components and Polymer at Software As Craft Philadelphia. Not knowing much about web components, I didn’t realize there was such an elegant way to encapsulate the related JavaScript, CSS, and HTML for a single piece of a website’s behavior without having to manipulate global state. That’s a huge improvement to the state of the art for front-end development, and I immediately wondered how easy it would be to start including this stuff in some of my current Rails projects.
If you’re not already familiar with the basic idea of web components and how they work from a purely front-end perspective, take a look at the introduction on the Polymer site. Otherwise, read on to learn about the mostly painless process I used to get web components up and running in an existing Rails app.
Asset Pipeline Support
In pursuit of a simple solution, I considered bypassing the asset pipeline and dumping some
components into the public directory. But it turns out that a single gem addition can teach
the asset pipeline how to package up web components just like other assets. The Emcee gem
seems to be really good at this, so I started by adding emcee
to my Gemfile and updating the
bundle. Next I ran Emcee’s generator to create a few useful files:
rails g emcee:install
That gave me a brand new manifest file for web components, similar to the familiar
application.js and application.css manifests, except this one’s an html file. Emcee also added
a reference to the new manifest file to my application layout. At this point I’m free to add
my own components to app/assets/components
, and they’ll get served up by the asset pipeline.
Installing a Component
But before diving into custom components, I wanted to see a stock component working in my app
as a proof of concept. The Polymer project includes lots of components, including a full set
that implement Google’s material design guidelines. I could download a component along with
each of its dependencies from the Polymer site and unzip them all into
app/assets/components
, but this process can easily be automated with a front-end package
manager like Bower, which is Polymer’s recommended way to install components.
I wasn’t already using Bower to manage any of my Rails assets, but it’s easy to install with node and npm. Installing those isn’t covered here, but they’re widely supported and should be easy to set up on any modern system that doesn’t have them already. Since I already had node and npm, installing bower was easy:
npm install -g bower
After that, any Polymer component and all its dependencies can be installed with a single
bower command. Emcee already created a .bowerrc file to tell Bower to put packages in
vendor/assets/components
, where they’ll be available to the asset pipeline but separate from
any custom components I might add to app/assets/components
. I decided to install a stock
Polymer component that implements a material-styled button:
bower install Polymer/paper-button
I also added it to the manifest at app/assets/components/application.html
.
*= require paper-button/paper-button
Using the New Component
Now the installation is done, and I’m free to drop the component’s custom HTML element (in
this case, <paper-button>
) into any view in my app, and the component will show up on the
page. Or at least it will show up in Chrome (more on browser compatibility in a moment).
<paper-button raised>My Button Text</paper-button>
After adding this code to a view, I have a button with its own “paper” styling and fancy
JavaScript behavior, and all this complexity is fully encapsulated behind the <paper-button>
tag. The button has complex CSS styles that look very different from the rest of my app, but I
didn’t have to change any of my existing CSS or selector names to avoid conflicts. The button
uses a web font that I didn’t previously have in my project, but I didn’t have to install that
either. And I don’t know or care if the button depends on a hundred different JavaScript
libraries, because I didn’t have to separately include them in my project. If you think this
sounds like a refreshingly modern way to manage front-end dependencies, you’re starting to
understand why web components are a big deal.
Supporting More Browsers
But at this point the component still didn’t show up in any browser other than Chrome. Luckily, Polymer provides a library of JavaScript workarounds that extend web component support to browsers that haven’t yet implemented all the necessary HTML features natively.
The library is called webcomponents.js, and Bower already installed it as one of
paper-button’s dependencies. I first tried including this library by referencing it in
app/assets/javascripts/application.js
. But this broke the Poltergeist-driven acceptance
tests in my test suite, because PhantomJS doesn’t get along well with webcomponents.js. So as
a temporary fix to keep my test suite passing, I had to include webcomponents.js in a way that
let me turn it off in test mode. I ended up adding a separate include tag to my application
layout:
<%= javascript_include_tag "webcomponentsjs/webcomponents" unless Rails.env.test? %>
And then I needed to add it as an asset to be separately precompiled. This went in
config/initializers/assets.rb
with the rest of my asset pipeline configuration.
Rails.application.config.assets.precompile << "webcomponentsjs/webcomponents.js"
After that, my paper-button looked perfect in Firefox and Mobile Safari as well.
Testing Issues and Future Topics
It’s a problem that web components don’t yet work well with PhantomJS (and probably other similar testing tools), because I will definitely need acceptance-level testing of web components once my app starts to really depend on them. And once I start creating custom components of any real complexity, I’ll also need to unit test them, which is something the Polymer team is actively working on. I’ll revisit these testing issues in a future post.
Also in a future post, I’ll look into the best ways for web components to interact with data that lives on the server side of a Rails app. But for now, if you’ve been following along, you’re ready to further explore the components described in the Polymer docs, as well as their tutorials on how to make your own custom components. Until next time, enjoy your trip into the future of web development.
comments powered by Disqus