Unlike page based web applications, Ajax applications tend to present a lot of functionalities within single view without reloading the entire page and thereby avoiding any changes to the browser’s history. This is extremely efficient in delivering page content but at the same time also gives an unique problem to Ajax application developers who wishes to allow their application users to switch between different states of application especially in a multi-step operation. Normally, it is done in multi-page web application by simply using browser BACK and FORWARD buttons. In the case of ZK, you can achieve this browser history management using bookmarks as described here with added advantage of being able to save them as browser bookmarks.

However, recently History API has been introduced as a part of HTML5 specification so I would like to introduce you in this blog post how you can use these History API within your ZK application to make them stateful. HTML5 API introduces two methods to history object, pushState and replcaeState, and a new event to the window, onpopstate.

For this article I have modified data filter demo from ZK live demo and using HTML5 history API I have made it to remember all search queries by adding them to browser history state.

Here is a demo of modified data_filter demo that uses HTML5 pushState API and onpopstate event.

This data_filter.zul example contains few textboxes to enter search terms. After clicking any of the filter buttons listbox shown below will display filtered data based on the terms entered in the textboxes. You can enter various filter terms, perform search and use browser BACK/FORWARD buttons to re-visit earlier search results. The basic idea is to save filter terms entered in the textboxes on browser history as a state when users click filter button. Now whenever you press BACK/FORWARD button browser will fire onpopstate event to the window object and pass the saved state. In the onpopstate state I extract this state object and use zAu.send(Event) API to send onSerach event to the controller that filters the data according to the filter terms passed as event data and updates the listbox.

Here is snippet from data_filter.zul showing one of the textbox and the associated filter button

				

...

Clicking on filter button sends an onClick event back to server and its event handler is invoked which filter the listbox data based on all three textbox strings.Here is a snippet from FilterGridController class

        //Apply this event listener to three filter buttons
	@Listen("onClick = #btnFilter1, #btnFilter2, #btnFilter3")
	public void getFoodByInput(Event event) {
		filterGrid.setModel(new ListModelList(FoodData.getFilterFoods(filter1.getValue(), filter2.getValue(),
				filter3.getValue())));
		refreshFilterGridFooter();
	}

This implementation is similar to original data_filter demo. But notice that I have added a client side onClick event handler as well to call pushState API to store filter parameter state as shown below

function setHistoryState(btn) {
			var f1 = zk.Widget.$('$filter1').getValue();
			var f2 = zk.Widget.$('$filter2').getValue();
			var f3 = zk.Widget.$('$filter3').getValue();
			history.pushState({ 'f1': f1, 'f2' : f2, 'f3' : f3}, "Search results", "?q=" + f1 + f2 + f3);
		}

This is a Javascript function that is invoked whenever you click on any filter buttons. Here I first use ZK client-side API to retrieve respective textbox values and store it as an object on browser history state using pushState API of HTML5. It takes three parameters: a JSON object containing whatever data you want to store as a part of state, a title and a url.

After few searches your browser should have saved few states along with the urls that we passed as the third parameter to pushState API. Notice that when you click filter button your browser url changes to the one passed in pushState API. Now when you click BACK button in you browser, it will fire an onpopstate event and pass state object as part of event. In our case I pass this state back to server by sending an onSearch event through zAu.send().

$(window).bind('popstate', function(event) {
			var prevState = event.originalEvent.state;
			if (prevState) {
				zAu.send(new zk.Event(zk.Widget.$('$filterGrid'), "onSearch", {'' : {'data' : prevState}}));
			} else {
			    // no state hence do nothing
			}
		});

On server side I listen to this event in our FoodGridController and filter the listbox based on the state received as part of event data.

@Listen ("onSearch = #filterGrid")
	public void onFoodByHistory(Event event) {
		JSONObject json = (JSONObject)event.getData();
		if(json != null) {
			JSONObject data = (JSONObject) json.get("data");
			filter1.setValue((String)data.get("f1"));
			filter2.setValue((String)data.get("f2"));
			filter3.setValue((String)data.get("f3"));
			filterGrid.setModel(new ListModelList(FoodData.getFilterFoods(filter1.getValue(), filter2.getValue(),
					filter3.getValue())));
		}
		refreshFilterGridFooter();
	}

You can donwload the complete source code for this example code from its github repository here

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.

2 Responses to “History management with HTML5 History API in ZK”

  1. Richard says:

    Thanks, Ashish.

  2. […] History management with HTML5 History API in ZK introduces how developers can use this API in ZK with JavaScript code. In this article, I will go through how you can use this API using Java code. This is done by introducing a ZK add-on “ZKPushState”, a Java wrapper that allow developers to handle this feature in pure Java code which makes managing history more in a more ‘ZK’ way and simplifies the difficulty of encoding/decoding state. […]

Leave a Reply