Skip to content

Instantly share code, notes, and snippets.

@tbroyer
Created February 22, 2012 10:07
Show Gist options
  • Save tbroyer/1883821 to your computer and use it in GitHub Desktop.
Save tbroyer/1883821 to your computer and use it in GitHub Desktop.
GWT PlaceHistoryHandler.Historian using HTML5 pushState and onpopstate
/*
* Copyright 2012 Thomas Broyer <t.broyer@ltgt.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.ltgt.gwt.place;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.place.shared.PlaceHistoryHandler.Historian;
/**
* An {@link Historian} using HTML5's {@code pushState} and {@code onpopstate}.
* <p>
* This code has only been tested in Firefox and Chrome, with
* {@link com.google.gwt.place.shared.PlaceHistoryHandler#handleCurrentHistory()}
* called from the {@link com.google.gwt.core.client.EntryPoint EntryPoint} (i.e.
* I don't know how it would work otherwise, wrt. Chrome firing an initial
* {@code popstate} event).
*
* @see https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history
* @see https://github.com/balupton/history.js/wiki/The-State-of-the-HTML5-History-API
*/
public class Html5Historian implements Historian,
// allows the use of ValueChangeEvent.fire()
HasValueChangeHandlers<String> {
private final SimpleEventBus handlers = new SimpleEventBus();
public Html5Historian() {
initEvent();
}
@Override
public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> valueChangeHandler) {
return this.handlers.addHandler(ValueChangeEvent.getType(), valueChangeHandler);
}
@Override
public String getToken() {
// your own logic here to extract the token from Window.Location
}
@Override
public void newItem(String token, boolean issueEvent) {
if (getToken().equals(token)) { // not sure if this is needed, but just in case
return;
}
// your own logic here to construct the new URI
pushState(newUri);
if (issueEvent) {
ValueChangeEvent.fire(this, getToken());
}
}
@Override
public void fireEvent(GwtEvent<?> event) {
this.handlers.fireEvent(event);
}
private native void initEvent() /*-{
var that = this;
var oldHandler = $wnd.onpopstate;
$wnd.onpopstate = $entry(function(e) {
that.@net.ltgt.gwt.place.Html5Historian::onPopState()();
if (oldHandler) {
oldHandler();
}
});
}-*/;
private void onPopState() {
ValueChangeEvent.fire(this, getToken());
}
private native void pushState(String url) /*-{
$wnd.history.pushState(null, $doc.title, url);
}-*/;
}
@tbroyer
Copy link
Author

tbroyer commented Aug 30, 2012

@dodgex
Copy link

dodgex commented Aug 22, 2013

the link is not available :(

[UPDATE] https://github.com/carlos-aguayo/html5gwthistory this seems to be the new url ! :)

@rdwallis
Copy link

rdwallis commented Oct 4, 2014

line 81: should be oldHandler(e);

@billy1380
Copy link

@leforthomas
Copy link

leforthomas commented Feb 15, 2018

I am implementing this is in a web app, it works fine until I reload the page. I am using the Html5 historian from Carlos' repository. You can easily reproduce the issue by clicking on "Mail" in the app and reloading the page. The server says "Error not found". To be honest I really wonder how this could work in the first place. Surely the server would be serving the web app from the full path. I am missing something re the HTML5 history API? example URL http://gwthistoryhtml5.appspot.com/mail/

@leforthomas
Copy link

OK, I researched this a bit... it is incredibly messy, happy to hear suggestions on how to improve this.

  1. I added a (jetty) server side redirect using a filter. If the request URI contains my place tokens then I use the dispatcher to forward to the base servlet. The issue is this will apply to ANY resource I am requesting, eg images, javascript, etc... which also means they won't be cached anymore because from the client side it is seen as different resources each time I query from a different state.
  2. Another issue that crept in is that it messes up the GWT.getHostPageUrl() which seems to hard code the fact that navigation is done using # and queries... I had to rewrite my code which made use of this handy method to extract the real base URL.
  3. I had to rewite part of the historian to handle deeper URLs, ie the ones that have a base host URL with a path.

I am really not happy with the results, mostly because of 1) and I really wonder it is worth the hassle. Happy to hear people's opinion on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment