Rich Internet Applications Using Backbone.js and Liferay
It’s 2014 and your portal needs to get RICH! It’s no longer enough to have a portal that just works and presents information to your end users from multiple different sources. Today, it’s just as important to have a rich and easy to use interface.
The tooling available for the front-end today is the best it’s ever been, so good that the front-end today can finally become a first class citizen on the web. Traditionally, the backe-end influenced how your application’s front-end gets built, what frameworks get used, how the builds and deployments are performed, and so on. In this article I’m going to show you how Veriday super-charged our Liferay portal development practices by using Backbone.js and other front-end tooling.
A little bit of history
Our engineering team at Veriday has been using Liferay for over 7 years now. We have seen Liferay grow from an up and coming player in the enterprise portal space into a best-in-class technology going head to head and winning against incumbents such as Websphere Portal, Oracle Web Logic and Microsoft Sharepoint.
Hello Backbone.js
Backbone.js is an extremely lightweight Javascript framework with a RESTful JSON interface based on the model-view-presenter design pattern. Today our engineering team has been exclusively using this framework for over 2 years now because it 1) allows us to iterate faster on the front-end and 2) is fairly flexible and integrates well with our web services based architecture.
So far we have used Backbone.js in two different ways. We utilize it when we’re building our products such as MapCMS. We also use it when building Liferay portlets. The pattern we use to integrate it with Liferay portlet diverges from Liferay’s portlet development documentation, but being able to diverge from the patterns described in the Liferay documentation allows us to also bring our own experience and lessons learned to the table.
The hybrid JSF/JSP + Backbone.js approach
We refer to the way we decided to integrate Backbone.js into our portlets the “hybrid” approach. The main reason is we still wanted the portal and portlet infrastructure, but at the same time we wanted the flexibility and richness we gain from single page applications. In this approach we build and deploy our portlet projects in the usual standard way, but the portlet just ends up being a shell for the Backbone application. In this method we also gain the advantage of being able to bootstrap the Backbone application without incurring the delay of performing an initial Ajax request to initialize the portlet. This is a very handy way to provide some contextual information to Backbone, for ex. is there a logged in user or who is the logged in user, or what is the current user’s locale preferences?
How do we load Backbone
So you can’t just use Backbone without drinking the rest of the cool-aid. Before we bring Backbone into the picture, we need to present some other libraries that make things more efficient and easier. The first library we need is Require.js for Javascript module loading and dependency management. Require provides the module ‘import’ mechanism from other programming languages like Java or C for example.
define( //The name of this module "types/Manager", //The array of dependencies ["types/Employee"], //The function to execute when all dependencies have loaded. The //arguments to this function are the array of dependencies mentioned //above. function (Employee) { function Manager () { this.reports = []; } //This will now work Manager.prototype = new Employee(); //return the Manager constructor function so it can be used by //other modules. return Manager; } );
The Require.js example above shows how it is used to define a module which you can then load using require within your portlets:
require(["some/script.js"], function() { //This function is called after some/script.js has loaded. });
So using the above pattern we can define JavaScript modules, and then load them in asynchronously using Require.js within each portlet’s JSF view or JSP.
What about HTML? Where does that go?
The second part of this puzzle before we get to all the Backbone goodness, is what do we do with our application’s markup? how do we load it? The first thing we decided here is there will absolutely never be any HTML within any JavaScript or event Java code. It’s very important for you to make this decision up front as it will help you avoid a whole lot of headaches as you maintain and enhance your portlets over time.
Now we introduce another library called Underscore.js – this library provides dozens of utility functions that we will need. One of these is the _.template() function.
define([ "text!templates/map-store-result.html", ], function(StoreResultTemplate){ ... renderStoreResult: function(store){ var templateHtml = _.template(StoreResultTemplate, { store: store } ); this.$el.find("#location-result").html(templateHtml); }, ...
In the example above we use Require to load a plain simple html file called map-store-result.html, and we use Underscore’s template method to parse the template and populate it with values – ex. a “store”.
<h3>{{store.getStoreName()}}</h3> <div class="span6"> Dealer: {{store.getOperatorName()}} <br /> Phone: {{store.getStorePhone()}} <br /> Fax: {{store.getStoreFax()}} <br /> Auto Service: {{store.getStoreAutoService()}} </div> <div class="span6"> {{store.getStoreAddress()}} </div>
The above HTML snippet is our template code referencing different attributes of a “store”.
Let’s take a look at some Backbone.js already
We now have all the pieces of the puzzle and can get Backbone loaded into a Liferay portlet, we know how to load our modules, and finally we also know where our actual application markup will live.
Models and Collections
Backbone provides the basis for models and collections that will store your data on the front-end, communicate with your back-end web service endpoints, and provide you with a mechanism to build event-driven front-end applications.
Views
The last piece is the Backbone View which handles all the front-end logic and business rules. View respond to user actions such as clicks, manage models on the front-end and synchronize them with the back-end. A typical view would 1) fetch some models or collections from your back-end, 2) allow the user to update the model and finally 3) send the model to the back-end to get saved all while keeping the actual view in sync with the model.
Event Driven User Interfaces
The key to a successful view pattern is to fully embrace event driven development. One pattern we use, is we introduce a “dummy” model for the client’s state. We then use that model to track and listen to state changes. The end result is a completely event driven. The event-driven approach also allows us to easily re-wire the events and callbacks to create a completely different user experience at a reduced effort.
var DocumentView = Backbone.View.extend({ events: { "dblclick" : "open", "click .icon.doc" : "select", "contextmenu .icon.doc" : "showMenu", "click .show_notes" : "toggleNotes", "click .title .lock" : "editAccessLevel", "mouseover .title .date" : "showTooltip" }, render: function() { this.$el.html(this.template(this.model.attributes)); return this; }, open: function() { window.open(this.model.get("viewer_url")); }, select: function() { this.model.set({selected: true}); }, ... });
The above code sample illustrates how simple it is to define an event-driven view. Furthermore, by simply altering the event callbacks or trigger we can easily change the user experience in our Liferay application.
Liferay is a powerful platform for deploying your rich applications on to, it is also pretty flexible in how you build your applications so don’t be afraid to diverge from their tutorials and documentations and include your own technologies and frameworks; especially when it comes to your portlet’s front-end.