After spending certain time on wep-app development on GAE I came to the point where I wanted more AJAX and more interaction on my pages. Playing with pure “home-made” AJAX development clearly shown me that:
- it is time consuming
- it is not ok with cross-browsers compatibility
Logically my first though was to look into GWT. But this time I decided not to eagerly start learning all the GWT controls and there layouting. Instead I wanted to get high-level impression on strengths and weaknesses of this technology.
Soon I figured out its main weakness or let’s say inconvenience which was referenced by many and many blogs/posts online by different people. GWT plays very nice when it concerns browsers support BUT it generates a single host HTML page where all other essential for your business logic UI is built dynamically by javascript. This may be a good option if you want to build you web application from scratch but in real world we often need to mix different parts. Especially I was interested in how I can integrate GWT components, or widgets like they call it, into my already existing JSPs.
Let me describe an example. Suppose I want to have a component called NewsBlock. Suppose also I want to place it in my JSPs by a custom tag and pass a parameter to specify news category for example. So it should be something like:
<div>
<gwt-newsblock category='Politics'/>
</div>
Then I want this custom tag rendered to GWT implementation of the NewsBlock component.
Internet search offered only naive and primitive solutions which required hardcoding ids and presumed NO parametrisation. Namely they proposed to do the next thing:
In HTML:
<div id='containerID'>
</div>
In GWT EntryPoint.onModuleLoaded() method:
RootPanel.get('containerID').add(anyNeededWidget);
But you see here a hardcoding of the container ID in my GWT code which DOES NOT allow to isolate components from outer world (HTML/JSP).
Next research revealed me that GWT creators provided a very powerfull tool that may give me some more space for experiment: JSNI.
JSNI builds a bridge between your outer context javascript and the javascript that lives inside GWT. It also allows you to add in GWT Java code native functions (i.e. inject pure javascript code into not-yet-compiled Java).
The main idea is to expose part of the GWT module API to the outer javascript world. If I could have in my GWT Java code base a method like public void addNewsBlock(String category); that actually adds a widget and which is callable from my JSP-generated javascript then the problem would be solved.
API can be exported in the following way:
public class Refucktoring implements EntryPoint {
private native void exportAPI()
/*-{
$wnd.addNewsBlock = @com.refucktoring.client.Refucktoring::addNewsBlock(Ljava/lang/String;);
}-*/;
public void onModuleLoad() {
exportAPI();
}
private static void addNewsBlock(String containerId, String newsCategory)
{
RootPanel.get(containerId).add(new NewsBlock(newsCategory));
}
}
Here 2 things are done:
- addNewsBlock(String, String) method is made visible to external javascript.
- containerId parameter is added to resolve IDs hardcoding problem. I will use this parameter at the point where my JSP custom tag will inject the GWT widget into some DIV element for example.
The nex step is integration in HTML. The very rude form to express it is:
<div id='containerId'>
<script type='text/javascript'>addNewsBlock('containerId', 'Politics');</script>
</div>
But there is a hidden obstacle: bootsrap sequence of the GWT module DOES NOT guarantee you that at the time when this HTML block evaluation will occur the GWT module is already loaded. So it may happen that when this short script will try to call addNewsBlock() method the onModuleLoaded() and its inner exportAPI() methos are not yet called and thus nothing will be added to your DIV.
So we need to add one more thing to allow GWT execute all this little outer javascrips itself after the module is loaded. Naturally would be good to handle this in the onModuleLoaded() method. Thus we add following to our EntryPoint class implementation:
public class Refucktoring implements EntryPoint {
private native void exportAPI()
/*-{
$wnd.addNewsBlock = @com.refucktoring.client.Refucktoring::addNewsBlock(Ljava/lang/String;);
}-*/;
private native void executeExternalInjections()
/*-{
$wnd.executeInjections();
}-*/;
public void onModuleLoad() {
exportAPI();
executeExternalInjections();
}
private static void addNewsBlock(String containerId, String newsCategory)
{
RootPanel.get(containerId).add(new NewsBlock(newsCategory));
}
}
Native method executeExternalInjections() is called automatically after exportAPI() to guarantee the proper sequence. That method calls external javascript function executeInjections() that should be added, as you already thought, by our JSP and is responsible for execution of all those widget-injections small scripts.
The last piece of the puzzle is actually implementation of that javascript method and nicely collecting this all together. Your JSP/HTML should add the following script to the HEAD of the document:
<script type="text/javascript">
var injectorsHolder = new Object();
function executeInjections()
{
for (var i in injectorsHolder)
{
injectorsHolder[i]();
}
}
</script>
<script type="text/javascript" language="javascript" src="refucktoring/refucktoring.nocache.js"></script>
As you see the sequence is important here: first preparatory script and only then GWT module loader script. Finally in places of injection (that can be generated by your custom JSP tags) you have this for example:
<div id="NewsContainer1">
<script type="text/javascript">
injectorsHolder['NewsContainer1'] = function() {addNewsBlock('NewsContainer1', 'Politics'');};
</script>
</div>
<div id="NewsContainer2">
<script type="text/javascript">
injectorsHolder['NewsContainer2'] = function() {addNewsBlock('NewsContainer2', 'Sport'');};
</script>
</div>
The framework is ready and you can do now with GWT literally what you want 
In the next post I will add an update to show the JSP custom tag implementation and show the overall solution working example.
Recent Comments