Sunday, October 3, 2010

GWT Widget in New Browser

GWT can create re-sizable, drag-enabled windows. However, these windows can be only displayed within the GWT application itself that is inside of a browser window. If your application needs to pop-up a widget  in a separate browser window, just like using “window.open” in javascript, then it is not as easy as opening a new url in a new browser window. Currently, I encounter a problem that my GWT needs to be in a IFrame and it needs to open a new window for displaying different widgets.

Well, there are several ways to do it. For example, you can develop another GWT entry point and use GWT Window.open to open that GWT module into another window. The other way is that you can use JSNI to invoke the javascript window.open() method. None of these methods are pretty and reusable. It also requires extra effort. Also we need to deal with the communicate between two windows. So with this in mind, we need to consider the design upfront. The best way, it is to reuse the same GWT module for all the browser windows, meaning that you load the same module into a pop-up window and let the module to decide when the widget will be displayed in the new window, when the user action is performed.

When we are loading a same module into a different window, there are couple things we need to consider. First is that if the module is very heavy, how we can have a minimum footprint in order to launch it faster. For example, in the example I show below, my main module require a connection to the server immediately in order to fetch the stock data, but it is irrelevant to the widget in the separate window. Secondly, we need handle the communication in between two windows. For the first one, I think the best way to design this is to use a stub which can toggle and display between main application and your pop-up widget. So the stub should have a small memory footprint in order to load it fast into different window. However, in the example I show here, I didn’t use the stub design instead, I develop some utility to handle the toggling because I didn’t know this limitation for GWT at the time and didn’t plan for it, but still that utility can be reused by other projects.  For the second issue, the only way is to use the JSNI. However, I will show you a way to make the code reusable, meaning that you don’t need to write the JSNI code every time for the new project.

First let’s look at the figure 1. When the user clicks on the magnify glass, a pop-up window shows up as shown in figure 2. Notice that the window is a little bit small to show all the contents. Figure 3, shows the whole widget content after resizing the window.  Note that, the widget is also got resized. Secondly, notice that, the symbol “c” is passed into input box in the pop-up window.
Figure 1.

Figure 2.


Figure 3.



Figure 2 shows the widget inside of the new window. The widget is a GWT dialog box. The position has been initialized to left, top corner. And the glass style for the background has been set to white color in order to cover the main application. If you decide to create your own window panel from GWT, which doesn’t allow to be dragged, then you don’t need worry about to setting the background glass. However, you need to handle the window resizing issue. Below is the code to show you the implementation.

Let’s first look at the WindowUtils.java


In the static block, it will create the script element and append the javascript into the document when the class is invoked. In this way, as I mentioned previously, I don’t need to write the javascript code every time for the new project. The javascript functions will be called by GWT JSNI. There are four functions:
getWindowUrl is called when using Window.open in GWT. This function will give me the current URL of the application when opening up a new window.
hasOpener is used to avoid unnecessary operations from the main application, we can use this to determine what needs to be loaded in the new window.
setValues and getValues are used for window to set and get the javascript variable externally from GWT in order to communicate between window and openers.
The rest of methods from this class are the JSNI wrapper call.

Now let’s look the usage of this class. I will show several places.
First, let’s look at the dialog presenter class below


Inside of the constructor, line 21, it will check to see if this widget is opened by the opener. If yes, then show the dialog. That is what you see in the figure 2.
Inside of the show method, from line 34, again it first check to determine if this is a opener instance by calling the hasOpener method. If it is not, that means we are still in the main window, then we will open the new browser and also set the javascript variable by calling setExternalWindowValues. After the new browser opened, this show method will be called from the constructor again and this time the hasOpener method will return true. The next thing is that, from line 43, it will do the following steps, get the value from the javascript variable by calling getExternalWindowValues in order to set that value into the correct place; show up the widget in the window; perform the lookup operation and finally it will register the window resize handler. Starting line 51, it is the inner class for the ResizeHandler. Basically, it is resizing the widget that is passed.

Let’s look at anther code fragment below that is using the hasOpener method.


This loadStock method is performed only when the main application is in the main browser window. Again in line 51, it uses the hasOpener method to determine whether the application is in main browser or new browser.

In conclusion, the windowUtils class can be reusable for other project and it direct injects the javascript code into the document so that we don’t need to write the same javascript over and over again. By loading the same GWT module into a different window, we also eliminate the need for creating another GWT module. Imagine if we have many different kinds of widgets that need to be displayed in the new window, then we will have to create many different GWT modules, which require to config different entry points in different gwt.xml files.

P.S. please visit my other blog for Google Analytics http://googleanalyticreport.blogspot.com/