Grails, jQuery, AJAX: do anchor navigation. Part 2, final
Full and incomplete pages
We continue the conversation about anchor navigation . Our goal is to make a working application on Grails.
There is one subtlety. I really want the page to be shown both in the full version (with a header, navigation, etc.), and in a shortened version (for AJAX calls). However, typing / my-app / do / receipts , we get the full version. Now it looks like this:

Oops! It is necessary to somehow distinguish between the situation when the page is the main and when it is internal. To do this, we write a small filter:
grails-app/conf/PartialPageLoadFilters.groovy
def filters = {
partial(controller: "*", action: "*") {
before = {
// Это AJAX-запрос?
if (request.xhr) {
// Нужно показывать как внутреннюю страницу.
request.partialPage = true
}
true
}
}
}
I note the use of the magic attribute request.xhr that Grails provides. Its presence means that the current request is called through AJAX. Most browsers report this to the server through a special HTTP-Header. Above, I just declared all AJAX requests as “internal”; in a real application, the situation can be more complicated.
Now I can use the request.partialPage flag everywhere . You can make a separate layout for the internal pages, but I preferred to just stupidly make such inserts in the main layout:
grails-app/views/layouts/main.gsp
%{-- Частичный вариант --}%
%{-- Полный вариант страницы --}%
...
...
...
We achieved full transparency (for the controller and GSP) in which of the page options to show.
Bookmarks and History
So, we have three screens that can be switched via AJAX:
/ my-app / # do / receipts | / my-app / # do / buy | / my-app / # do / feedback |
---|---|---|
![]() | ![]() | ![]() |
Everything is very cool, but as it turns out, our “dynamic” links cannot be bookmarked. The fact is that they contain an anchor tail, which is not transmitted to the server . Therefore, when loading such a URL, the server does not know which of the internal pages to show.
Only anchor knows about anchor. This begs the solution: first load the base page with JavaScript, then run the code that defines the current anchor and load the inside using AJAX. Without particularly bothering, I wrote this code:
web-app/js/application.js
$.ready(function() {
$('#pageContent').html('Загружаем...')
.load(buildURL(document.location.hash), function() {
// Перерисовываем ссылки
updateNavLinks();
});
});
This code only demonstrates the idea. If desired, this code can be added to a more beautiful state, but the essence will not change: first you have to download the basic wrapper, then the user will have to wait for the internal part to load. For example, Facebook usually doesn’t show anything at all until the inside is loaded - a matter of taste.
But what about the story? On Back / Forward transitions, our $ .ready event will not fire. The browser considers such transitions to be a “hop” from one anchor to another, i.e. just scrolling the page. No uniquely identifiable JavaScript events occur. What to do?
One way to solve this (quite brutal in itself) is to periodically check the document.location.hash propertyfor changes. If suddenly the current anchor has changed, you need to reload the inside of the page.
function checkLocalState() {
if (document.location.hash && document.location.hash != currentState) {
currentState = document.location.hash;
$('#pageContent').html('Ссылка изменилась, загружаем...')
.load(buildURL(currentState), function() {
// Перерисовываем ссылки
updateNavLinks();
});
}
}
Now check the anchor change every 500 milliseconds:
$.ready(function() { setInterval(checkLocalState, 500); });
Now our application will respond to Back / Forward transitions, but with a delay of half a second. This delay (sometimes annoying to the user) can be seen on many large sites using a similar navigation scheme. If you reduce the interval, background JavaScript will eat more resources. If the interval is increased, the reaction time will increase. In general, you have to pay for everything.
Summary
We created a Grails + jQuery application with AJAX navigation, which:
- Reloads only the internal content of the page without reloading the entire page.
- Correctly saves the page state in the address bar, i.e. Suitable for bookmarks and sharing links with friends.
- Correctly responds to Back / Forward transitions in the browser.
- Allows you to develop server code "in the old way", without bothering with the new rules of the game, because all page loading logic is transparent (for controllers and GSP pages).
- Links can be opened in a new window and they will work correctly.
- I note that the links in our application are no different from regular links, except for the pound sign ( # ) at the beginning of the URL! After all, we carefully carried out all the logic of the links in jQuery-code.
Of course, in a real application, the code given here will have to be improved, in particular, more accurately take into account situations of the absence of an anchor, use a more complex link building scheme, etc. However, I hope that he successfully demonstrates the idea and solves most of the problems with anchor navigation.
References: