Ruby
Article
By Hendra Uzia

Integrating Jade Templates into Rails for Cleaner Templates

By Hendra Uzia

jade

Rendering dynamic content in a web application can be done using many tools, one of them is using a Javascript Template Engine. It uses a language with specific markup designed to render dynamic content to the Document Object Model.

Most of the markup used by javascript template engines resembles HTML, but some go further by introducing a new markup designed with a specific goal in mind. Jade has a unique characteristic compared to other javascript template languages. It simplifies the markup by removing the use of angle brackets and closing tags.

In this article, I will be demonstrating how to integrate Jade into a Ruby on Rails application. As an example, the application will be twitter-like, including timeline and user profile features to demonstrate the integration.

Some code examples in this article are taken from the example application, and some are simplified code taken from the code example. The completed version is available at Github, and there is also an online version available to demonstrate how it looks.

Introduction to Jade

Let me start by introducing you to the basics of Jade. After reading this section, you should be able to understand the basic construct of Jade. This, however, does not try to replicate the Jade documentation. Instead, I will highlight the most frequently used features in Jade so that you can get up and running fast.

It’s always good to start with what you know. Here is a comparison between ERB (Embedded Ruby), Jade, and the generated HTML. The following code is taken from the completed version of my tweet template implementation. A quick glance at the following code should be able to give you a glimpse of how Jade differs from ERB:

ERB

<li class="tweet">
  <%= link_to tweet.user do %>
    <img src="<%= tweet.user.thumbnail %>" class="tweet__photo" />
  <% end %>
  <div class="tweet__body">
    <div class="tweet__info">
      <%= link_to tweet.user.name, tweet.user, class: "tweet__name" %>
      <%= link_to "@#{tweet.user.username}", tweet.user, class: "tweet__username" %>
      <span class="tweet__date timeago" title="<%= tweet.created_at.iso8601 %>"></span>
    </div>
    <div class="tweet__text"><%= tweet.text %></div>
  </div>
</li>

Jade

li.tweet
  a(href=tweet.user.links.self)
    img.tweet__photo(src=tweet.user.thumbnail)
  .tweet__body
    .tweet__info
      a.tweet__name(href=tweet.user.links.self)=tweet.user.name
      a.tweet__username(href=tweet.user.links.self) @#{tweet.user.username}
      span.tweet__date.timeago(title=tweet.created_at)
    .tweet__text=tweet.text

Generated HTML

<li class="tweet">
  <a href="/@johnappleseed">
    <img class="tweet__photo" src="/path/to/image.jpg">
  </a>
  <div class="tweet__body">
    <div class="tweet__info">
      <a class="tweet__name" href="/@johnappleseed">John Appleseed</a>
      <a class="tweet__username" href="/@johnappleseed">@johnappleseed</a>
      <span class="tweet__date timeago" title="2015-11-30t01:30:10z"></span>
    </div>
    <div class="tweet__text">The quick brown fox jumps over the lazy dog</div>
  </div>
</li>

A look at the above code shows a distinct difference between Jade and ERB. Jade looks simpler and cleaner than ERB. This is due to the absence of closing tags and angle brackets in Jade.

Tag Name, ID, Class, and Attributes

From the previous template example, you can see that Jade uses tag name, ID, and CSS literals to create the DOM structure. Attributes are similar to those in HTML, but their values are just regular javascript. And because divs are the most common choice of tag, it is the default tag if you skip the tag name.

The following code:

li.tweet#tweet(title="tweet")
  .tweet__body

Will render to:

<li id="tweet" class="tweet" title="tweet">
  <div class="tweet__body"></div>
</li>

Buffering and Interpolation

Inside the template you can do all sorts of javascript operations, such as buffering code to be written to the template and interpolating strings. Consider the following example. Let’s assume you pass the tweet object with the following structure:

var tweet = {
  text: "The quick brown fox jumps over the lazy dog",
  user: {
    name: "John Appleseed",
    username: "johnappleseed",
    thumbnail: "/path/to/image.jpg",
    links: {
      self: "/@johnappleseed"
    }
  }
};

You can generate a link to a user as follows:

a.tweet__name(href=tweet.user.links.self)=tweet.user.name

The above will generate the following HTML:

<a class="tweet__name" href="/@johnappleseed">John Appleseed</a>

If you want to interpolate a string, you can use the familiar #{} operator:

a.tweet__username(href=tweet.user.links.self) @#{tweet.user.username}

And it will render the following HTML.

<a class="tweet__username" href="/@johnappleseed">@johnappleseed</a>
--ADVERTISEMENT--

The Integration

Integrating Jade into Rails requires a handful of steps. The installation steps are pretty straightforward, but as of this writing, there is a compatibility issue with the latest version of Rails. There is a workaround for this issue, please see the installation instructions for more detailed information.

Installation

To have a working installation of Jade in a Rails application, please put the following gems into your Gemfile:

# Gemfile
gem 'ejs'
gem 'jade-rails', '1.9.2'

And require Jade runtime into your application.js:

# app/views/assets/javascripts/application.js
//= require jade/runtime

As noted previously, as of this writing, there is a template compilation issue for Rails version 4.2.5. Please replace the jade-rails gem with my github branch:

# Gemfile
gem 'jade-rails', github: 'hendrauzia/jade-rails', branch: 'rails-4-2-5'

As a side note, the above source as of this writing haven’t been merged into jade-rails gem, but in the future this may have been rectified. Please consult the jade-rails releases to see if the fix has been applied.

Templates

Templates must be put in a specific place in your application. You need to put them inside app/assets/javascripts/templates, and give them the .jst.jade extension. You can also add another preprocessor to your templates, such as: .erb if necessary.

See the following example:

# app/assets/javascripts/templates/tweet.jst.jade
li.tweet
  ...

Here is a small tip if you need to put image assets in the template: Add ERB to your template preprocessor and add the .erb extension to the end of the template name. This Way, the assets can be rendered using the proper digest. For example, you might want to provide a default image when a user doesn’t have a picture:

# app/assets/javascripts/templates/tweet.jst.jade.erb
li.tweet
  a(href=tweet.user.links.self)
    if tweet.user.thumbnail
      img.tweet__photo(src=tweet.user.thumbnail)
    else
      <%= image_tag "users/default.jpg", class: "tweet__photo" %>

And don’t forget to require your template in your application.js:

# app/assets/javascripts/application.js
//= require templates/tweet

The rest of the template features can be found in the Jade documentation. But before you decide on doing any fancy stuff, please read the remainder of my introduction.

Rendering

The template rendering capability in Jade is provided by the ejs gem. To render the template, use the following statement and put it in your javascript file. Using our previous example, if we want to render the template, we need a tweet object to pass into the template rendering function:

var tweet = {
  text: "The quick brown fox jumps over the lazy dog",
  user: {
    name: "John Appleseed",
    username: "johnappleseed",
    thumbnail: "/path/to/image.jpg",
    links: {
      self: "/@johnappleseed"
    }
  }
};

var htmlString = JST['templates/tweet']({ tweet: tweet });

The above code will generate the following HTML:

<li class="tweet">
  <a href="/@johnappleseed">
    <img src="/path/to/image.jpg" class="tweet__photo">
  </a>
  <div class="tweet__body">
    <div class="tweet__info">
      <a class="tweet__name" href="/@johnappleseed">john appleseed</a>
      <a class="tweet__username" href="/@johnappleseed">@johnappleseed</a>
      <span class="tweet__date timeago" title="2015-11-30t01:30:10z"></span>
    </div>
    <div class="tweet__text">the quick brown fox jumps over the lazy dog</div>
  </div>
</li>

If you need further customization, like attaching event listeners to DOM or otherwise messing with the DOM prior to appending to a container, you can create a jQuery object from the DOM and preprocess it.

For example, here we try to attach timeago to the tweet date:

var htmlDOM = $(JST['templates/tweet']({ tweet: tweet }));
htmlDOM.find(".timeago").timeago();

Once everything is set and done, you can append the rendered HTML to any container that you want. In this case, let’s say we want to put at the end of the timeline:

$('.timeline').append(htmlDOM);

Limitations

Integrating Jade in Rails is not without its limitations. There are features in jade that are not available in the client. For example, includes and extends are not available in the current stable jade-rails gem. As a workaround, you can manually appending the generated HTML to other generated HTML using javascript.

Conclusion

Jade is a simple and clean javascript template engine. It can be integrated easily to a Rails application and works nicely with the asset pipeline. Rendering the templates is a breeze and doesn’t require a lot of configuration, but still it is not without limitations. Jade should serve common needs in rendering javascript templates, it’s ease of use and smooth integration certainly outweigh it’s limitations.

  • Nodari Lipartiya

    How does this relate to Slim?
    Is it faster/slower?. I use Jade with Node, and Slim with Rails. The syntax is pretty similar.

Recommended
Sponsors
Get the latest in Ruby, once a week, for free.