Basically there are 3 part of the solution:
- Adding the server side support to keep a history of the AJAX calls being made.
- Add and initialize Dojo to your web application.
- Send back button click events to load the previous state of the UI on the server side.
So the first step is to create a session-scoped History bean that will maintain a queue of the states. Define 4 simple apis:
- addToBack - adds the previous state to the history
- addToForward - adds the current state to the history
- doBack - go to the previous state
- doForward - go to the state ahead
public class HistoryBean {
private List_backHistory = new ArrayList ();
private List_forwardHistory = new ArrayList ();
public HistoryBean() {
super();
}
public void doBack() {
if (_backHistory.size() == 0) return;
State state = _backHistory.remove(0);
addToForward(state);
}
public void doForward() {
if (_forwardHistory.size() == 0) return;
State state = _forwardHistory.remove(0);
addToBack(state);
}
public void addToBack(State state) {
_backHistory.add(0, state);
}
public void addToForward(State state) {
_forwardHistory.add(0, state);
}
public State getCurrentState() {
if (_backHistory.size() == 0) return null;
State state = _backHistory.get(0);
return state;
}
}
Next add and intitialize the back button module from Dojo into your application web pages. You can find more information on Dojo and back button here.
<script type="text/javascript" src="js/dojo/dojo.js"
djConfig="preventBackButtonFix: false">
</script>
<script type="text/javascript">
// load the dojo.back package
dojo.require("dojo.back");
dojo.back.init();
</script>
Now create a state object to insert into the browser's history. The state provides 2 functions, one for back and the other for forward. Finally set the initial state when the page is first loaded.
<script type="text/javascript">
// define a state for the back button clicks
var state = {
back: function() {
invokeBack();
},
forward: function() {
invokeForward();
}
};
// load the initial state
dojo.back.setInitialState(state);
</script>
Now add two A4J based JavaScript functions that are invoked from the state functions above. These functions will changes the state at the server side when the back or forward buttons are clicked.
<a4j:jsFunction name="invokeBack"
action="#{history.doBack}"
reRender="__my_tabs"/>
<a4j:jsFunction name="invokeForward"
action="#{history.doForward}"
reRender="__my_tabs"/>
In these A4J javascript functions you are doing two things, first asking the server's session to switch to teh previous or next state in the queue. Secondly re-rendering the necessary components through the "reRender" attribute.
That's all you need to get the back button functioning correctly in an application built Ajax4JSF/RichFaces. I am pretty sure you can apply the same to other JSF implementation like MyFaces and JSF RI to get it to work.
You can download an example app that shows the back/forward button in action from here.
Things that you should be careful about in the server side history implementation.
- Avoid loops in the state management. For example, when back in invoked it should call a setter that will call back into the history to add a back state.
- Try to store objects with very small footprints in the state to conserve memory. You can also implement a FIFO model to remove very old back states.
- Add an api like loadState() to the State interface (see sample code) that will allow you to implement the state loading in different parts of your application.