; last updated - 9 minutes read

Singe-page applications have become tremendously popular in the last couple of years. They are fast, they are responsive and they can save network bandwidth. The first SPAs that caught the attention of the community were based on JavaScript. So nowadays everybody seems to believe you can't write an SPA with JSF.

But you can. It's not even difficult. And it pays: the application becomes much more responsive. This article shows two approaches how to do it with BootsFaces. I'm sure you can do the same with other JSF frameworks like PrimeFaces, too, but I didn't test it yet.

Currently, there are at least two approaches. You can exploit BootsFaces AJAX to do your navigation, or you can add AngularJS to the equation. The latter approach requires you to learn both Angular and JSF, but it may be useful because it allows you to use client-side AngularJS components.

But first things first.

What is a Single Page Application?

A single page application is an application that doesn't use full-page refreshs for navigation. Instead, SPAs use JavaScript to exchange parts of the DOM tree to navigate to another dialog. That doesn't necessarily mean there's no server access. It's possible to load the entire application at startup time. But that approach doesn't always work. Little is won when the application is super-fast - but takes five minutes to boot.

So modern SPA frameworks like Angular load HTML snippets when navigating to another dialog. The snippets tend to be small and static, so it's a fast server request. And it's a lot faster than non-AJAX navigation. The AJAX request loads a single HTML snippet. Maybe it also loads a small CSS file and a Json object. But that's it. A non-AJAX request loads every CSS file, every JavaScript file, the entire HTML file and possibly even fonts. That takes a while. Some time ago I had to maintain a JSF application running on Liferay doing 125 request on every page navigation. When we added AJAX, this went down to half a dozen requests. Luckily, most application server don't add as much overhead. Our showcase application starts 40 requests per navigation, give or take a few. The CombinedResourceHandler of OmniFaces reduces this to 9. AJAX navigation requires only a single request, plus a request for every image on the page.

The second advantage of SPAs are that the browser doesn't have to re-render the entire page. Nor does it have to run all the JavaScript files. In the case of Angular 1.x, initializing Angular costs a couple of seconds. And that's only one of your libraries. Initializing the other JavaScript libraries takes some time, too.

We've designed BootsFaces to be fast, but even so, switching from traditional navigation to the SPA approach makes the application feel a lot faster.

The simple JSF-only approach

Forget everything you've learned about JSF templates. JSF SPAs work the other way round. Instead of embedding your JSF page into a template, the main page actively includes the JSF fragments. Putting it a bit more technical, you replace <ui:composition /> by <ui:include />. Here's a very simple main page written with BootsFaces 0.8.1:

Let's examine line 19 first. It's an include statement which loads a page fragment. The nice thing about JSF include statements is that they don't have to be static. They are allowed to use EL expressions. So we load the name of the current fragment from the navigation bean, which happens to be a very simple class:

@Named @SessionScoped public class NavigationBean implements Serializable { private String page="start.xhtml"; // plus getter and setter }

The other interesting part are the AJAXified navlinks (lines 9-14 of the XHTML file). That's a feature of BootsFaces 0.8.1. We haven't published this version yet, so this post is sort of an announcement post. However, I expect we will publish this version very soon. In the meantime, we've published developer snapshots at MavenCentral, so you can already give it a try. See our description on how to get the developer snapshot.

Basically, the AJAXified navlinks don't do much, either. They call a setter to update the name of the current JSF page fragment. After that, the content area - that's basically the <ui:include /> - is re-rendered.

The JSF pages to include are not full-blown JSF pages, but only JSF fragments containing the code you want to put into the content area:

...

That's it!

Demo source code is available at my GitHub repository. Feel free to convince yourself!

Troubleshooting

There's a small but annoying catch. BootsFaces tries to keep the number resource file loaded with each request small. So you have to load every CSS and JavaScript file at startup time. Luckily, that's easy: add a hidden panel to the index.xhtml and add every component you need:

...

Alternatively, you can use a custom theme. Most custom Bootstrap themes I've seen are all-in-one files including the CSS and JavaScript code of every component. Or rather, of most elements: several components of BootsFaces require CSS or JavaScript files that are not part of the Bootstrap distribution files. By the way, you can also include the Bootstrap default theme as custom theme.

Update Jan 14, 2016: What about the history?

I just read Stefan Tilkov's flame "Why I hate your Single Page App". It's a highly recommended read, and it showed me the shortcomings of my approach: there's no bookmarkable URL, no URL history, no working "back" button. I opened an issue on the BootsFaces bug tracker to solve this problem.

I don't think it's a big deal to implement an advanced <b:include /> tag, but until then, I'd like to give you a hint how to solve the problem yourself:

  • Have a look at history.js. That's a small JavaScript library that allows you to rewrite the current URL. Use this on every AJAX request to add a fragment to the URL. For the sake of simplicity, you can use the name of the JSF file to include as fragment name.
  • Use the PreRenderViewEvent to set the target page within the navigation bean.

Currently, I don't have the source code at hand, but I've done precisely this in the past, so I know you can do it, too.

But there's also a simpler approach. Well, it's debatable whether it's simpler or not, but it doesn't require you to rewrite the URL yourself. Let AngularJS do the trick for you.

The sophisticated AngularJS approach

Currently, I'm reluctant to recommend AngularFaces because it doesn't support Angular2 yet. Nonetheless, using the AngularJS router is another interesting approach to turn a JSF application into an SPA.

Like above, forget everything you've ever learned about JSF templates. You need to write a start page that actively include JSF page fragments. AngularFaces uses AngularJS 1.3, so the include is done by an ng-view directive:

The AngularJS controller uses a traditional AngularJS router, so there are no surprises here:

var navigationApp = angular.module('navigationApp', [ 'ngRoute', 'navigationControllers' ]); navigationApp.config([ '$routeProvider', function($routeProvider) { $routeProvider.when('/hidingColumns', { templateUrl : '2_layout/hidingColumns.jsf', controller : 'EmptyController' }).when('/', { templateUrl : '1_intro/start.jsf', controller : 'EmptyController' }).otherwise({ templateUrl : '99_miscellaneous/other.jsf', }); } ]);

When I investigated this approach, I was pretty surprised that JSF isn't limited to delivering complete JSF pages. It can also render HTML fragments. So the page snippets to be included by the AngularJS router look like so:

...

A nice side effect is that these page snippets contain less syntactical clutter than a traditional <ui:composition />.

Using Angular allows you to use client-side components. In this example, I've added a navigator widget to the page:

...

To navigate to another JSF page (or rather: JSF snippet), you add another fragment to the browser URL. For example, navlinks looks like so:

It goes without saying that you should avoid AJAX with the AngularJS approach. I've spent a lot of time to enable AJAX with AngularFaces, but still, I believe it's better to replace AJAX by native AngularJS code.

Beware of the memory leak

There's a small drawback to any SPAs, including JSF based SPAs. If your JavaScript code contains a memory leak, it grows over time. In particular, it's very important to destroy jQuery widgets after disposing the part of the DOM they are used by. The PrimeFaces team told me a couple of years ago that they've solved their JavaScript memory leaks. Currently, these memory leaks are an open ticket in our BootsFaces bug tracker. I believe the memory leaks isn't that bad. You can work a long time before noticing. But still, until we've solved the issue, you should be aware of the problem. Of course, every non-AJAX navigation or simply pressing the F5 key solves the problem for the next couple of hours.

Wrapping it up

Turning JSF applications into SPAs has many advantages. The applications are faster and much more responsive. There's less load on your server and the network. And there's another advantage: most developers who are new to JSF have a hard time to understand the templating concept. My approach to SPAs replaces JSF templates by a technique almost every developer is familiar with: include files. So newbies will need less time to get familiar with your JSF pages.


Comments