Ruby on Rails – using Handlebars To Generate Bootstrap Modals
I’ve been learning Ember.js over the course of the past year and it’s given me ideas that I’ve been pleased to be able to use in Ruby on Rails projects as well; one such idea is using Handlebars.
I’ve been aware of JS templating languages for years but either did not see a need to use them, or perhaps didn’t quite realize the possibilities. I’ve recently had a few projects where a large number of partials were being rendered to a page, and each needed it’s own Bootstrap modal – which involves a fair bit of DOM – including a large image –– this led to pretty poor performance.
I came to the realization that it would make more sense to load in the modal content only on demand with an XHR request.
Normally in a scenario like this I might have been inclinded to either build a string of HTML at runtime in javascript, or to use a js.erb view to render a partial and insert it using jQuery. In this case though you’d be either building a fairly intense HTMl string in jQuery, or re-rendering the same partial for the modal countless times.
That line of thinking led to wonder if perhaps Handlebars could improve this scenario. I was pleased to see that I can now send back only a bit of JSON from the server and use that JSON to render a Handlebars template.
I started by looking into using Mustache, but realized that I needed to iterate through associated records, and so Handlebars made more sense.
It worked nicely and dramatically sped up the page performance.
Gemfile – I’m using Active Model Serializers to produce the JSON response, and Bower-Rails to install Handlebars:
gem 'active_model_serializers', '0.8.3'
gem 'bower-rails'
Install the gems:
$ bundle
$ spring rails g bower_rails:initialize
Add Handlebars to Bowerfile:
asset 'handlebars'
Install Handlebars:
$ bundle exec rake bower:install
Add Handlebars to application.js:
//= require handlebars/handlebars
items.js - this is where Handlebars comes in:
$(document).ready(function () {
var source = $("#modal-template").html();
itemModaltemplate = Handlebars.compile(source);
});
$(document).on('click', '[data-trigger="modal"]', function () {
var item_id = $(this).data('target');
$.getJSON('/items/' + item_id + '.json').done(function (data) {
var html = itemModaltemplate(data.item);
$('#item_modal').remove();
$('body').append(html);
$('#item_modal').modal({show: true, background: true});
});
});
application.html.haml - render the modal template:
= render "items/modal"
items/_item.html.haml:
%a.btn.btn-info{data: {trigger: 'modal', target: item.id}} Info
items/_modal.html.erb:
<script type="text/x-handlebars" id="modal-template">
<div class="item-modal modal fade in" id="item_modal" role="dialog" aria-hidden="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header"><h4 class="modal-title">Title</h4></div>
<div class="modal-body">Body</div>
<div class="modal-footer">Footer</div>
</div>
</div>
</div>
</script>
serializers/item_serializer.rb:
class ItemSerializer < ActiveModel::Serializer
has_many :other_things
attributes :id, :title, :bg_image
def bg_image
object.cover.url(:original)
end
end
controllers/items_controller.rb:
def show
@item = Item.find(params[:id])
respond_to do |format|
format.html
format.json { render json: @item, serializer: ItemSerializer }
end
end
css:
.item-modal {
display: none;
}
Edit: I’ve found that this process can be cleaned up and made a bit more elegant with the help of the handlebars_assets gem which moves the handlebars templates out of Rails views, and into assets/javascripts/templates where they graduate to proper .hbs template files, which are automatically precompiled as a part of the asset pipeline. This reduces the process of manually compiling each template as I’ve done above within $(document).ready(). Both ways work just fine, but especially where a number of different Handlebars templates are going to be used, the gem may be the better bet.
I am available for Ruby on Rails and Ember consulting work – get in touch to learn more.