One of the challenges of a recent project was maintaining the browser history while someone navigated the application. This was a problem because the browser back button always took the user to the most recently viewed page. Our application has two layers of navigation. The top-level layer is between the three pages: home, search results and details.  The secondary level of navigation is within the search results page to view multiple pages of results and the details page to view details one-by-one instead of having to go back to the search results to select another list item. We wanted the browser back button to emulate going back between the three top-views and we provided navigation buttons on the search results page and details page to navigate within those pages.

Sencha Touch provides full browser back history support, so that if the browser back button is tapped, the application will go back to the previous URL in the history list.  This is useful because when running an app on an Android phone, the browser back button is available.  However, Safari can hide the back button if the app is launched in web application mode.  For the latter reason, we made a back button available within the application.

Initially, we coded the app back button to mimic the browser back button behavior, in which it would go back through the history until it reached the app launch page. This was accomplished by adding actions to the Sencha Touch Ext.app.History.actions array.  However, we found that it would be more useful to be able to use the back button to switch back and forth between the search results page and the details page. A home button was provided to get the user back to the initial page to start a new search. In addition, the search results page and the details page each had navigation buttons to go forward and backward through the list of results and the details, respectively. The navigation between the pages would reflect the most recent details page viewed, by highlighting the record on the search results page and by navigating back to that view when returning to the details page.

Search Results page

Search Results page

Details page

Details page

Back to Search Results Page

Back to Search Results Page

This all worked fine except for the case when the browser back (or forward) button was tapped instead of the app back button. Then the behavior reverted back to going back and back and back (and possibly out of the app). This was particularly a problem for us because we implemented a state-machine to keep track of which pages had been viewed so that the app could display a back button from the main page if necessary or in the case of a permalink taking a user directly to a details page, the back button would not be displayed at all, as there was no page to go ‘back’ to.

App Startup page

App Startup page

Startup from Permalink URL

Startup from Permalink URL

Home selected from Permalink

Home after Permalink

Navigating between pages in our application was accomplished by using the routes configuration in the page controllers and Ext.RedirectTo().  The redirect function populated the Ext.app.History.actions array and the windows.history.   We also used the function animateActiveItem() to animate the transition between pages.

Ideally, we would have disabled the browser history back button while in the app. However, since we couldn’t do that, we analyzed what was happening in the code and mitigated the effect by the following solutions:

1. Cleared the History actions list on a new search. The action list would be cleared back to the action that represented the search (home) page. This accesses a documented  private object  (Ext.app.History) in the Sencha Touch library.

To view the actions list in the debugger, we wrote a function called showHistory():

showHistory()
History actions 
	showHome  
	showListings listings 
	showDetails details/1 
	showDetails details/2 
	showDetails details/3

2. Minimized using Ext.redirectTo. In this case, only once for the Search Results page. The Details page used it for each selection of a details view. When going back to previous views, the history.go(-x) was used, where x is calculated based on the number of details pages that were viewed .  We used Ext.redirectTo during development to emulate requests to the server providing the data. As we started to integrate the data server, we found that we didn’t always have to call it to redisplay data that had already been loaded.  We were able to manipulate the page view based on what had already been viewed.

3. Captured change events on the Application.getHistory() object. If the current state did not match the current url, then a ‘back’ state event was consumed on the state machine to attempt to match up the current state with the url.   This is a weak ‘hack’ that must be corrected in a future release.

We also considered detecting changes on the windows.popstate event, but it fired when an action was added or removed from the history array.  This created the challenge of keeping track of whether the state had just been pushed to the history list or if it was being popped during a change to a different page. We possibly need to reduce our state machine to allow us to use this properly.

Using the popstate event in conjunction with the pushState and replaceState methods is supposed to allow you to mimic URL redirection without actually redirecting.  This would allow the application to be one element in the browser history. We are investigating how to accomplish this in our next project.

References

HTML5 Rocks Presentation

Sencha Touch History Support

Sencha Touch Ext.app.History

Dive Into HTML 5