Home Overview Demos/Webs News Download/Install Docs/Support Investors/Partners Commercial Tutorial: Simple ItsNat (Core) Application Tutorial: Simple ItsNat (Core) STATELESS Application Tutorial: Single Page Interface SEO Compatible Web Site With ItsNat STATEFUL Using HASHBANGS Tutorial: Single Page Interface SEO Compatible Web Site With ItsNat STATELESS Using HASHBANGS Tutorial: Single Page Interface SEO Compatible Web Site With ItsNat STATEFUL Using HISTORY API Tutorial: Single Page Interface SEO Compatible Web Site With ItsNat STATELESS Using HISTORY API Tutorial: Hybrid Client-Server Centric Programming
Tutorial: Single Page Interface SEO Compatible Web Site With ItsNat STATEFUL Using HASHBANGS

Last update: 2015, Sept. 27  

Introduction

Introduction/Setup
How can a web site be SPI and page based at the same time?
Web application set-up
Creating the ItsNat servlet
Main page processing
Main page processing stateful
Main page processing and hashbangs
Infrastructure of fundamental states
Support of Google Analytics to monitor fundamental states
Fundamental states: overview and overview.popup
Fundamental state detail and secondary state "More detail"
Fundamental state not_found
Conclusion
Download and Online Demo

The Single Page Interface (SPI) paradigm is not new, these days SPI is very popular on "web applications" because modern web frameworks with AJAX support make this kind of applications easier than ever.

However ItsNat is a web framework strongly focused on Single Page Interface web sites (and applications).

This tutorial shows how to build SPI web sites, web sites based on pages can be evolved to SPI avoiding unnecessary reloading without losing the benefits of page based web sites like bookmarking, Search Engine Optimization (SEO) or accessibility (JavaScript disabled) according to The Single Page Interface Manifesto.

There are several options to make a SPI web site with ItsNat, it may be stateful or stateless, based on hashbangs (#!) or using History API.

This tutorial shows how to make an ItsNat SPI web site stateful using hashbangs.

In this tutorial we will be able to make SPI web sites with SEO, bookmarking, back/forward buttons (history navigation) and with some tricks we still can use "page visit counters" for instance Google Analytics in spite of Single Page Interface user navigation. These will be explained with code.

To understand this tutorial some basic knowledge of ItsNat is required.


How can a web site be SPI and page based at the same time?


In spite of recent support of JavaScript by Google Search when crawling web sites, the best approach to get a high rank in search crawlers is to ignore JavaScript. If JavaScript is ignored no AJAX requests are executed then your site is not SPI, this is fine for web crawlers. ItsNat is a technology able to make web sites navigated with pages when JavaScript is ignored and Single Page (no reload, no page navigation, page navigation is simulated) when JavaScript is executed.

The typical behavior of ItsNat is the following: when the DOM in server changes, JavaScript code is automatically generated and sent to the client to update the client DOM accordingly. Usually in web sites many elements are shared like the header, footer, styles, JavaScript libraries etc and the content area is almost the unique zone being fully changed, in SPI this "content area" can be changed with new HTML fragments, a HTML fragment is dynamically inserted into the DOM document in server using W3C DOM Java API, in the same time this new HTML code is also automatically inserted by JavaScript code in client (usually using innerHTML), unfortunately this approach is not SEO friendly because web crawlers may ignore JavaScript.

To provide page simulation one ItsNat feature is the key: fast-load mode.

If ItsNat is configured in fast-load mode (the default mode), when the initial page (based on a pure HTML template) is being loaded, any DOM change performed in server to the initial template by developer code is not sent as JavaScript, DOM is serialized to generate the initial HTML page being sent to the client. Hence the same user code manipulating DOM can generate JavaScript or plain HTML depending on the phase is executed, when an AJAX event is received (JavaScript) or on load time of the initial page (HTML), inserting the required HTML fragment into the content zone avoiding the typical "two sites" approach (site for end users and site for crawlers).

When fast-load mode is disabled, the template being used is the markup being sent to the client and DOM changes in server are sent as JavaScript DOM operations (this is not SEO friendly and not recommended for SPI SEO compatible web sites). In this tutorial we will use of course fast-load mode.

In a SPI web site your clicks usually replace parts of the web page with new fragments using AJAX (ItsNat is extremely powerful to do this task using pure HTML fragments), when JavaScript is ignored clicked links are executed following the href attribute. The href usually define the URL of a "page", ItsNat will load this page as usual, by this way the web crawler see the web site "paged". And alternative onclick is present for Single Page navigation (AJAX based).

If the href of a link (a navigation component in general) defines the URL of a bookmarkable page, this page is "fundamental state" following the SPI Manifesto terminology. A fundamental state can be crawled as a page and in the same time, you can navigate to this state with no page reload when JavaScript is not ignored (onclick is executed), in both cases, page navigation ignoring JavaScript or AJAX state navigation, the final state of the browser page will be the same. This is the art and magic of Single Page Interface SEO compatible web sites made really easy with ItsNat.

In this tutorial we are going to use a parameter in the query part of URL in links to specify the fundamental state/page ?st=state and in the same time hashbangs (#!st=state) to provide state navigation with no reload when JavaScript is executed. As you know, we can change the URL manually or using window.location adding a # part to the URL with no page reload, this technique allows us to make bookmarks, and web crawlers also recognize them for bookmarking.


Web application set-up


ItsNat does not require special set up or application servers, any servlet container supporting Java 1.6 or upper is enough. Use your preferred IDE to create an empty ItsNat web application, this tutorial uses spistfulhashbangtut as the name of the web application and 8080 port.


Creating the ItsNat servlet


ItsNat does not impose a bootstrap, there is no "default" ItsNat servlet, in fact you can use several servlets using ItsNat in your web application. Using your IDE add a new servlet class with name servlet (this name is not mandatory) in the default package (again not mandatory).

Default servlet registration in web.xml is valid, this example does not require special initialization parameters or filters in web.xml.

According to this setup the URL accessing our servlet is (8080 is supposed):

http://localhost:8080/spistfulhashbangtut/servlet

Because our web site is SPI we would like a prettier URL like

http://localhost:8080/spistfulhashbangtut/

We have two options:

  1. Add servlet as the welcome file in web.xml:
  2. Add a simple index.jsp (this file is usually by default the "welcome file") with this content: In web.xml:

Now replace the generated code of the servlet class with this code:

As you can see our servlet now inherits from HttpServletWrapper:

This ItsNat class redirects any request to the ItsNatHttpServlet object wrapping the servlet instance.

Web application configuration is done in the standard init(ServletConfig) method, configuration is ItsNat is "classic", imperative, calling configuration methods.

This call sets 4 documents with server state by user session, this value tries to avoid abusive users of opening too many browser windows with the same page because these pages are isolated in server (no shared data between pages).

This call is not really necessary because fast-load mode is the default, this method is called to clearly state fast-load mode is mandatory in SPI web sites with page simulation.

The folder WEB-INF/pages is going to be used to save "ItsNat page templates", because our web application is SPI only one page template is need, later another very simple page will be added to monitor page visits. Into the folder WEB-INF/fragments will be saved "page fragments", page fragments are pure HTML pages where only the content of <body> (or <head>) is used.

SPITutGlobalEventListener is a global org.w3c.dom.events.EventListener, all AJAX requests received by this servlet (for any document loaded by this servlet) are first dispatched to this listener. This is the code:

When the web browser of an end user sends an event from a client document lost in server (usually because the user session is expired), the method ItsNatEvent.getItsNatDocument() returns null because this event is "orphan", there is no document in server attached to the client document (is expired and removed in server). In this case we ask the end user to reload the page, you could reload without asking.

The call:

is not really needed, it prevents other global event listeners of being called (there is no more in this tutorial).

This SPITutGlobalEventListener class is not actually needed because the default behavior of ItsNat processing an orphan event is to automatically reload the page (the only difference is that end user is not asked).

Following in the servlet:

Registers a global document (page) listener. This listener is called when a page (=document) load is requested or any other unknown page request (not AJAX events). Source code:

If the request includes the default parameter itsnat_doc_name and the specified page template exists the method ItsNatServletRequest.getItsNatDocument() returns a non-null ItsNatDocument object, if the URL requested does not include itsnat_doc_name is a "custom" request and getItsNatDocument() returns null. The later is the case of pretty URLs, this tutorial does not use itsnat_doc_name in public URLs.

We first check whether the parameter ganalyt_st is present, this parameter is going to be used later for state monitoring using Google Analytics. If this parameter is not present is the expected case of a request like this:

http://localhost:8080/spistfulhashbangtut/

In this case we have to load the main page of our web site, so itsnat_doc_name is specified with value main as a request attribute, the value main is the name of the main page template of our SPI web site, ItsNat checks first if itsnat_doc_name was specified as request attribute and then as request parameter. Now we are ready to re-send the request to ItsNat calling:

This new request internally has a similar effect as the explicit URL:

http://localhost:8080/spistfulhashbangtut/servlet?itsnat_doc_name=main

Now the target page template is specified and found and request.getItsNatDocument(); returns a not null value and nothing is done in this case because this global listener is not the typical place to manage normal page load requests.


Main page processing


Returning to the servlet:

This call registers with the name main the page template file main.html (saved in WEB-INF/pages/):

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:itsnat="http://itsnat.org/itsnat">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="expires" content="Wed, 1 Dec 1997 03:01:00 GMT" />
    <meta http-equiv="Cache-Control" content="no-cache, must-revalidate" />
    <title id="titleId" itsnat:nocache="true">Tutorial: Single Page Interface SEO Compatible Web Site With ItsNat STATEFUL Using Hashbangs
    <link rel="stylesheet" type="text/css" href="css/style.css" />
    <script type="text/javascript" src="js/spi_hashbang.js?timestamp=2015-08-18_01"></script>
    <script type="text/javascript">
    function setState(name)
    {
        if (typeof document.getItsNatDoc == "undefined") return; // Too soon, page is not fully loaded
        var itsNatDoc = document.getItsNatDoc();
        var evt = itsNatDoc.createUserEvent("setState");
        evt.setExtraParam("name",name);
        itsNatDoc.dispatchUserEvent(null,evt);
    }
    window.spiSite.onBackForward = setState;
    </script>
</head>
<body>

<div class="main">
    <table style="width:100%; height:100%; padding:0; margin:0;" border="0px" cellpadding="0" cellspacing="0">
    <tbody>
    <tr style="height:50px;">
        <td>
            

Tutorial: Single Page Interface SEO Compatible Web Site With ItsNat STATEFUL
Using Hashbangs

</td> </tr> <tr style="height:40px;"> <td> <table style="width:100%; margin:0; padding:0; border: #ED752A solid; border-width: 0 0 2px 0; "> <tbody> <tr class="mainMenu" itsnat:nocache="true"> <td id="menuOpOverviewId"> Overview </td> <td id="menuOpDetailId"> Detail </td> <td> </tr> </tbody> </table> </td> </tr> <tr style="height:70%; /* For MSIE */"> <td id="contentParentId" itsnat:nocache="true" style="padding:20px; vertical-align:super;" > </td> </tr> <tr style="height:50px"> <td style="border-top: 1px solid black; text-align:center;"> SOME FOOTER </td> </tr> </tbody> </table> </div> <iframe id="googleAnalyticsId" itsnat:nocache="true" src="?ganalyt_st=" style="display:none;" ></iframe> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-2924757-6', 'auto'); ga('send', 'pageview'); </script> </body> </html>

As you can see ItsNat templates are pure HTML files because view logic is coded with W3C DOM Java API. By default templates are cached, that is, DOM nodes are internally serialized as plain HTML and shared between users; any cached DOM subtree is replaced in server by a text node containing a special mark, when this subtree is sent to the client ItsNat automatically sends the cached markup. DOM caching is interesting for "static" (server point of view) parts of the template, caching can be fully avoided with a configuration flag but not recommended if you want to save server memory and improve performance.

Because we want to modify some parts of the page template, these parts are considered not static (because we need to access them programmatically and maybe change them) and must be marked as "not cached" with the special ItsNat attribute itsnat:nocache="true" where itsnat prefix is declared in <html> as xmlns:itsnat="http://itsnat.org/itsnat", yes ItsNat requires XHTML namespaces but your templates are usually going to be HTML 5 (take a look to the DOCTYPE declaration) and served with text/html MIME.

For instance:

The title of the SPI web site will change when a new fundamental state is loaded (page load time or AJAX event).

This row contains the web site main menu, is not cached because we need to access menu items in server to change the color of the current selected option.

This table cell is the parent of the "content area", when the end user clicks some menu option, this zone will change accordingly.

Finally:

The URL of this <iframe> will be changed to keep track of fundamental states when doing intra Single Page navigation, just adding the state name to the end of src URL, then the <iframe> content is reloaded (because URL has changed) and Google Analytics detects this reload. Inspecting the different parameters (states) we can track how the end user navigates the site in spite of SPI nature. In practice <iframe> URL will be changed using location.reload() to avoid new unnecessary history entries.

And:


<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-2924757-6', 'auto');
  ga('send', 'pageview');

</script>

The last script for Google Analytics in the end of the page is needed to know how users reach the site (it is the same script than loaded by the iframe), later intra Single Page navigation is monitored by the iframe.

The following code:

    

Is being used to send ItsNat "user events", a user event is an ItsNat extension of W3C DOM Events and is fired calling some public ItsNat methods from JavaScript. User events are used in this tutorial to notify the server about the next fundamental state to be loaded, these user events are received by a user event listener in server previously registered. In this example we use "user events" to navigate between fundamental states, because this tutorial is stateful state navigation can be also performed done with conventional AJAX based ItsNat event listeners.

An example of how fundamental states are changed is this dual link (a menu item):

When end user clicks this link the onclick inline handler is called, the call setState('overview'); sends a user event to command server to load the new fundamental state overview, because onclick returns false the default behavior of the link (process href) is aborted hence href="?st=overview" is ignored and the URL remains the same but the page has been partially changed with the new state. This is not the case of search engine crawlers, these bots usually ignore JavaScript and follow the link loading a new page with overview as the initial state according to this example. This is an example of dual link AJAX/normal.

Now is the time of registering a load listener for the main template (back to the servlet):

The SPITutMainLoadRequestListener.processRequest(…) method will be called when the servlet receives a new load request of this template, one call per load request. The source code is:

A new SPITutMainDocument instance is created per call (load request), this instance holds an ItsNatHTMLDocument object which represents the client document (page) being loaded, this instance will be the same and synchronized to client whether the page change its state with no page loading (this tutorial is stateful, an alternative stateless is possible).

This object is not going to be garbage collected because some event listener is going to be registered in ItsNatHTMLDocument and this document instance is automatically hold by ItsNat following the life cycle of the client document: when the end user leaves the page the document in server is automatically discarded. If no unload event is received ItsNat automatically discards documents in server when no event is received for a long time (the session expiring time is used) or when the user session expires (in spite of ItsNat developers do not use sessions, in ItsNat stateful mode servlet sessions are the basic mechanism to identify pages of end users), anyway a limit of alive pages (documents) exists to avoid abusive users, when this limit is surpassed oldest surplus documents in server are discarded.

The instance of SPIMainDocumentConfig is the configuration object of our mini ItsNat SPI framework stateful based on hashbangs created for this tutorial.

This mini-framework has three packages containing the following classes:

  1. org.itsnat.spi: generic classes

    Classes: SPIMainDocumentConfig, SPIMainDocument, SPIState, SPIStateDescriptor

  2. org.itsnat.spistful: stateful specific

    Classes: SPIStfulMainDocument, SPIStfulState

  3. org.itsnat.spistfulhashbang: stateful and hashbang processing specific

    Classes: SPIStfulHashbangMainDocument

You can use this mini framework to create other similar SPI web sites (stateful using hashbangs) with minimum code, the mini-framework is developed to be reused and extended.

The package org.itsnat.spi contains the classes going to be used for all SPI SEO compatible tutorials (stateful and stateless, hashbang and history api).

Let's explain in detail

The class SPIMainDocumentConfig is very simple, it is a configurator object for the document manager. This is the code:

In this configuration object we configure the necessary DOM elements to change the title, active menu, Google Analytics and a brief description of fundamental states.

For instance the menuElemMap collection maps state names with the DOM elements of the menu items, we need these elements to change their appearance when a menu option is selected, menu selection changes the current fundamental state being shown.

The contentParentElem is useful to insert/remove the markup of the "content area", googleAnalyticsElem is used for monitoring state visits with Google Analytics.

The most interesting class of the mini-framework is SPIMainDocument. Some explanation is interesting to show you how to manage states and how to support Google AJAX crawling based on hashbangs (read this and this).

This class is the core of the web site, tightly associated to the main template and responsible of (fundamental) state management, states directly dependent on main menu.

The first sentence:

Saves the ItsNat document object in an attribute because SPIMainDocument is a wrapper (and manager) of the ItsNatHTMLDocument.

Especially interesting is the method SPIMainDocument.registerState(SPIState) which stablishes the current state in client:

The call:

changes the page title adding the state description.

This code sends to the client some JavaScript code to specify the fundamental state being loaded in client in the URL of the page. The JavaScript method setStateInURL(stateName) is included into the file spi_hashbang.js, for hashbang based web sites, and spi_hsapi.js for history api ones, one of these files must be included in the main template and ever present in our SPI web site.

The class SPIStateDescriptor is just a state descriptor:

The class SPIState is the base class of state classes.

The boolean parameter indicates whether the state is "main level", in this case overview.popup is not "main level", is a second level state dependent of overview state, when selected the state overview.popup the menu item "Overview" is selected because only the "Overview" part is used for menu activation.

All states inherit from SPIState:

The call:

is specially interesting, this method calls SPIMainDocument.registerState(SPIState) which establishes the current state in client.


Main page processing stateful


Now is time to step down to describe how the mini-framework manages a stateful web site.

There are only two classes into the package org.itsnat.spistful: SPIStfulMainDocument and SPIStfulState.

The class SPIStfulMainDocument extends SPIMainDocument and concretes how states are managed in a stateful web site.

Let's to explain:

This code registers the user event listener listening state changes when some navigation link is clicked, for instance when the end user click a menu option. User events are received by the handleEvent(Event) method of this class. JavaScript client code will fire these user events to change the fundamental state, managed in server and propagated to client.

The method changeState(String) is the responsible of state change management. This method clears the old fundamental state with code like currentState.dispose() and ItsNatDOMUtil.removeAllChildren(config.getContentParentElement()), loads the specified fundamental state inserting the new markup into the content area, changes the appearance of the new active menu option and delegates further state processing to the appropriated SPIState class.

The class SPIStfulState just add one abstract method required to dispose the state because is required by SPIStfulMainDocument.


Main page processing and hashbangs


Time to another step down to know how the mini-framework is able to manage hashbangs.

The package org.itsnat.spistfulhashbang has only one class SPIStfulHashbangMainDocument, yes this class inherits from SPIStfulMainDocument and specifies how to manage hashbangs.

This demo application is prepared to be crawled by Google Search bots following its AJAX Crawling Specification, links ending with #! are also followed by Google bots, in this case the target site is accessed replacing #! with a parameter _escaped_fragment= followed by the text following #!. For instance this link is crawled by Google bots requesting with this link. Other web crawlers like Bing also support Google AJAX Crawling Specification.

When navigation the SPI web site, automatically #!st=somestate is added to the URL accordingly, you can copy this URL and use it to link the state/page in your web sites, when Google bots traverse your web site, Google knows that a URL ending in a hashbang must be accessed using the _escaped_fragment convention.

In this tutorial we are using another kind of state navigation based on a conventional st=somestate parameter in dual links, when Google traverses a dual link, the href, containing a st=somestate parameter, is used to navigate to the target page. This parameter is not shown when end users navigate using a conventional browser with AJAX enabled because the onclick is executed instead. This normal parameter is interesting for crawlers with no support of _escaped_fragment convention and to provide navigation with JavaScript disabled. This approach is optional, if you just want to support crawlers with _escaped_fragment, a hashbang href="#!st=somestate" is enough for Google Search to index your site if you provide the _escaped_fragment convention. This tutorial will use the st=somestate parameter in dual links.

When the st parameter is present when loading the main page, the value is the initial state. When no st parameter is provided, for instance when loading with this URL http://localhost:8080/spistfulhashbangtut, the default state is loaded (overview in this tutorial) and the default menu option is selected ("Overview").

Hashbang management in client happens in the JavaScript library spi_hashbang.js, this script is part of the mini-framework for SPI web sites based on hashbangs (stateless or stateful).

This is the source code of spi_hashbang.js:

Some code is interesting and needs explanation:

When you explicitly write down in your browser a URL like this http://localhost:8080/spistfulhashbangtut/#!st=somestate, the server only receives http://localhost:8080/spistfulhashbangtut/ without the fragment/hashbang, this is normal and expected, the hashbang is not a normal parameter, is not sent to server by web browsers. The server returns and sets the default state (overview in this tutorial), but the first time the JavaScript code automatically detects this case (initialURLWithState attribute) and sets the user specified initial state. Web crawlers have not problems because links are traversed using ?st=somestate or when a URL with #!st=somestate is detected the _escaped_fragment_ is used to fetch the page.

In a conventional web site users are used to page navigation by clicking Back/Forward buttons, most of the time this action is "automatic", ask to some user not to click the back button, is near impossible. A SPI web site MUST support history navigation by user actions, fortunately history navigation managed/simulated by the JavaScript code of our mini-framework.

In our example Back/Forward buttons are supported with no reload because window.spiSite.onBackForward was registered in main.html with the call:

    <script>
    function setState(name)
    {
        ...
    }

    window.spiSite.onBackForward = setState;
    </script>

When clicking Back/Forward buttons is detected, the state change in the hashbang of the URL is forwarded to the server by calling the method registered in window.spiSite.onBackForward with the name of the state obtained from URL. In this tutorial the user defined method setName sends a user event to server to set the new state name. By this way our Single Page Interface remains pure SPI including Back/Forward behavior (manual history navigation in general). If arbitrary state change transitions are too complex without reloading the page, use the default approach (page reload) leaving window.spiSite.onBackForward undefined.


Infrastructure of fundamental states


Now is the time to deep inside a concrete example using our mini-framework. We must to define the fundamental states being used as examples in this SPI web site.

Back to the servlet:

The fragment overview contains the markup in <body> being included in the content area of the main page when user selects the "Overview" menu option or the URL loading our web site specifies overview as the initial state. The same for detail.

The fragment overview.popup is going to be inserted into the overview fragment, the same for detail.more. There is a big difference between overview.popup and detail.more is that overview.popup is registered as a fundamental state, that is, it is bookmarkable, detail.more is just a substate no bookmarkable (is not a fundamental state).

This is why overview.popup is registered and detail.more is not, do you remeber SPITutMainLoadRequestListener?

The substate detail.more is not registered as a fundamental state.

The boolean parameter indicates whether the state is "main level", in this case overview.popup is not "main level", is a second level state dependent of overview state, when selected the state overview.popup the menu item "Overview" is selected because only the "Overview" part is used for menu activation (this is the reason of why we specify the separator character).

This is the source code of the concrete SPITutMainDocument inherited from SPIStfulHashbangMainDocument:

Later we will explain the SPITutState*, all of them inherit from SPIStfulState.


Support of Google Analytics to monitor fundamental states


The last two lines of SPIMainDocument.registerState(...) are:

Changes the URL of the current page of the <iframe> being used for Google Analytics adding the name of the new fundamental state, because location.replace() is used, the page is reloaded registering in Google Analytics the changed state, but according to replace() behavior, no new entry in the history is added (avoiding unnecessary history entries). By this way you can monitor how many times (and who and how) end users have been visiting the fundamental states of your SPI web site.

The parameter ganalyt_st is detected in SPITutGlobalLoadRequestListener and redirected the request to load the google_analytics template registered in servlet with the following call:

This template (google_analytics.html) only contains scripts of Google Analytics (of course the concrete token value is not valid for you):

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="expires" content="Wed, 1 Dec 1997 03:01:00 GMT" />
    <meta http-equiv="Cache-Control" content="no-cache, must-revalidate" />
    <title>Google Analytics
</head>
<body style="margin:0; padding:0;">

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-2924757-6', 'auto');
  ga('send', 'pageview');

</script>

</body>
</html>

This file could be a simple html (or JSP) file outside ItsNat control, but ItsNat ever adds response headers to disable page caching in browsers (a good thing to force and ensure fresh reload and script execution).

Note the call:

This configuration call sets this page template as non-scriptable, ItsNat does not add JavaScript code to the page for client-server synchronization purposes hence there is no AJAX, furthermore, the ItsNat document in server only is created and used in load time then is discarded because this page is stateless (server point of view).


Fundamental states: overview and overview.popup


Concrete fundamental states (inherited from SPIStfulState) are SPITutStateOverview, SPITutStateOverviewPopup, SPITutStateDetail.

SPITutStateOverview and SPITutStateDetail are directly launched by SPITutMainDocument because both are the main menu options. SPITutStateOverviewPopup is very interesting because in spite of it is a sub-state of SPITutStateOverview we want this state ("Overview showing a popup window") to be a fundamental state, that is bookmarkable, content reached by search engine crawlers and monitored by Google Analytics, so it is inherited from SPIStfulState because the SPIMainDocument.registerState(SPIState) method must be called.

Now we are ready to show SPITutStateOverview:

To understand what this class is doing we need the overview.html fragment template, the markup contained in <body> is inserted into the "content area" of our web site when overview state is selected (the markup in <head> is not inserted):

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:itsnat="http://itsnat.org/itsnat">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Overview
</head>
<body>

    

Overview

This "fundamental" state is processed by crawlers of search engines (Google, Yahoo, Bing...)

To understant how this SPI web site is "seen" by search engines, disable JavaScript in your browser.

You can load a new text like a pop-up window, the link used to open this pop-up contains a URL specifying as fundamental state this state with the popup already loaded, this URL is used by crawlers because "return false" is not executed. By this way text contained in pop-up is also processed by crawlers.

Show popup
</body> </html>

Because overview is the default state the following picture shows this state as the initial state of our web application:


Pay attention to the link:

This link is dual, AJAX and normal, in this case we directly bind an event listener in server with the call:

When clicked, the sub-state overview.popup (also a fundamental state) is instanced, and registered as the current state, showing a popup modal window with some text.

When this link is reached by web crawlers is also processed trying to load the linked web site with the initial state overview.popup because onclick handler is ignored. In SPITutMainDocument we know this fundamental state is a sub-state of overview so one SPITutStateOverview object is created with popup parameter set to true:

Then the popup window is automatically shown in load time, and because ItsNat is in fast-load mode the markup in the popup is rendered and sent to the client as markup, hence reached by search engine crawlers.

The class SPITutStateOverviewPopup loads the fragment with some markup and insert this markup to the page on top of a "modal layer":

This is the template file (overview_popup.html) associated to the name overview.popup:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:itsnat="http://itsnat.org/itsnat">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Overview Popup
</head>
<body>

    

Overview Popup

Overview + popup is also a fundamental state so this text is processed by search engine crawlers (Google,Yahoo,Bing...), because you can reach this state on load time with a URL like this (you can find this text in the end of the page loaded).

Click to exit

</body> </html>

Fundamental state detail and secondary state "More detail"


SPITutStateDetail is another fundamental state and also contains a sub-state ("More Detail"), however in this case this sub-state is not fundamental, not bookmarkable, hence there is no new class inherited from SPIStfulState and there is no call to SPIMainDocument.registerState(SPIState) when this sub-state is reached.

Source code of SPITutStateDetail:

And the template fragment detail.html:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:itsnat="http://itsnat.org/itsnat">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Detail
</head>
<body>
    

Detail

This "fundamental" state is processed by crawlers of search engines (Google, Yahoo, Bing...). The link below is used to load some new text, in this case the new state is not fundamental (is secondary) and the new text cannot be reached by crawlers of search engines.

More Detail
</body> </html>

Now the link is pure AJAX based and the markup with more info ("More Detail") only is inserted when end users click the link, therefore new inserted markup is not reached by web crawlers and there is no call to set this state as bookmarkable, following The Single Page Interface Manifesto this state (showing the "More Detail" text) is a secondary not a fundamental state.

The fragment with name detail.more is the file detail_more.html:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:itsnat="http://itsnat.org/itsnat">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>More Detail
</head>
<body>
    
    

More Detail

This text cannot be reached by search engines, because there is no fundamental state registered including this fragment on load time.

</body> </html>

Fundamental state not_found


Finally we have the state not_found, this state is provided to show a "state of error" when the main page is being loaded with a state name unknown (for instance an old bookmark saved before a web site redesign changing state names). When this not_found state is reached the <body> content of not_found.html is inserted:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:itsnat="http://itsnat.org/itsnat">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>State Not Found
</head>
<body>

    

State Not Found

</body> </html>

Conclusion


This tutorial has shown a generic example of how to build SPI stateful using hashbangs web sites with ItsNat similar to page based counterparts without sacrificing the typical features of the page paradigm like bookmarking, SEO, JavaScript disabled, Back/Forward buttons (history navigation), page visit counters etc. When a web site is being ported to SPI, pages are usually converted to states, these states can be fundamental and not fundamental (secondary). This tutorial has shown how fundamental states are very similar to pages including templating design based on pure HTML with all of benefits of SPI and how we can set as fundamental state virtually any state thanks to the server-centric nature of ItsNat, The Browser Is The Server approach and the fast-load feature.

Most of the code in this tutorial is infrastructure code, the mini-framework can be reused in many projects, this is the fixed cost of a SPI web site, any new fundamental state just require a new plain HTML template some minimal registration code and a new class inherited from SPIState, this class could be empty or be shared (the same class) for all fundamental states with just static content. In summary, the cost of adding a new fundamental state with only static content is almost the same as adding a simple HTML file like in page based development, with the advantage of no care and no repetition of headers, footers and, in general, static content outside of the content being added when changing states, avoiding the burden of repetition in the case of page based development, improving the end users experience thanks to the SPI open to highly interactive code by using AJAX events.

This is not the only approach, there are other alternatives like stateless and/or using History API, other tutorials show these options.


Download, Online Demo and Links


See running online

Download source code in GitHub.


Discussion about this tutorial at JavaLobby

Discussion about this tutorial at JavaHispano (in Spanish)



Terms of Use Privacy Statement Contributor Agreement Add to deli.cio.us Single Page Interface logo ItsNat logo Innowhere logo