Software Design UK


Home Page
Contact Us
About Us

Ajax Tutorial
GWT concepts
GWT Tutorial

Clients
Client Login
Autoresponder
 
GWT Tutorial
Home Page > Training > Tutorials > Web 2.0 > GWT Tutorial

This tutorial sets out how a programmer can use GWT to produce a website. It is aimed at programmers with little or no Java experience, but with reasonable experience with other programming languages using object oriented programming.

It is VERY STRONGLY RECOMMENDED that anyone who has not used GWT before reads the "GWT concepts" section before this section. It will take between 15 -3- minutes to read, and should save between 1 - 2 days frustration looking for information that is already to hand.

Using this tutorial

For this tutorial, the following directory structure is assumed:

Your java code : ~/clients/gbpAjax/src/com/sduk/client
GWT java jar : ~/download/GWT
GWT ui classes (in the jar) : com.google.gwt.user.client.ui


The GWT website produced by the code below is at:
www.greatbritishphotographers.com


The source java codes can be downloaded (right click and "save link as") from:

Structure
The main program (with menu items)
The Sink
The Sink List

Content
The Home Page Text
Nigel Cohen page
Bruce Denny page
Tom Jones Berney page
The Contact page


The "client" base html and css files can be downloaded from:
The Base html page
The stylesheet gbpAjax.css


GWT Programming Concepts

Your program, typically, conceives a page as a series of panels. It may be a single panel, onto which other panels are placed, or a series of panels. You place a series of Widgets (objects that relate to something you would find on a webpage, such as text, a button, a list of hyperlinks or textbox) on the panels. Then you define where you want the Widets to appear. And that is just about it.

You add panels to a page, and widgets to a panel, using Java code.

Each widget (which includes a Panel) is an object that is derived from a number of base classes. For example:

UIObject : the base class with properties or methods:
  • Size
  • Width
  • toString


Widget : derived from UIObject (so has its properties and methods), extended by:
  • attached
  • parent


FocusWidget : derived from Widget (so has its properties and methods), extended by:
  • ClickListenerCollection
  • addClickListener


Button : derived from FocusWidget (so has its properties and methods), extended by:
  • getHTML
  • setHTML


So through a series of inherited commands, the Panel object is instructed by GWT to deliver its HTML. Before delivering its own html, it instructs its children objects to deliver their html. Before their delivery, they also ask their children. And so on, until there are no unpolled children. In this way, the panel's html incorporates all the required html for all of its children.

So as a programmer, all you need to do is to slap down some panels, add some widgets containing the text, buttons, menu items, response to button clicks etc that you want and save it.

The command to "compile" the program takes all the Java objects you have created in your code, pulls out the required html and programmed actions and creates a final html container with a large amount of Javascript that writes the html you instructed through your Java objects.


The structure programs

The main program that calls all the other pages is gbpAjax.java

The entry point for the program is the onModuleLoad() method. A slightly santised version of which is:

// Central panel
private DockPanel panel = new DockPanel();

// Internal variables for the menu items
protected SinkList list = new SinkList();
private SinkInfo curInfo;
private Sink curSink;
private DockPanel sinkContainer;

// Internal variables for HTML text
private HTML description = new HTML();

public void onModuleLoad() {

loadSinks();

// The panel to contain the menu items
sinkContainer = new DockPanel();
sinkContainer.setStyleName("ks-Sink");

// The panel for the html to the right of the menu items
VerticalPanel vp = new VerticalPanel();
vp.setWidth("100%");
vp.add(description);
vp.add(sinkContainer);

// Add the menu items to the "West" side of the central panel
list.setWidth("120px");
panel.add(list, DockPanel.WEST);

// Show the initial screen.
showInfo();
}

}


The very central part of this code is setting up the central panel onto which all Widgets will be placed, called "panel". It is of type "DockPanel", which means you can dock subpanels onto it and define where they go with a command like panel.add(list, DockPanel.WEST). This means put the "list" Widget on the West (left) side of the DockPanel. The command list.setWidth("120px") sets the width of the "list" Widget to 120 pixels. The whole of the rest of the space of DockPanel (ie. to the right of the "West" area) is devoted to whatever else is placed on DockPanel (in the absence of a Widget being added to the panel to the North, South, East or Central areas).

Besides the "list" being added to the "panel", there is an "HTML" Widget called description and a "VerticalPanel" Widget called vp that are both added to the panel. Since "description" is placed first, it takes up the entire top part of the remaining space. As soon as all of its contents are displayed, the contents of "vp" will be displayed.

At this stage, "description" and "vp" are both empty. They will be filled by subsequent programs. But since the objects have been positioned here, all the subsequent programs need to do is to fill the objects with text or whatever they want, and the text will be displayed by the program.

The first line of the method is loadSinks()

protected void loadSinks() {

list.addSink(Info.init());
list.addSink(NigelCohen.init());
list.addSink(BruceDenny.init());
list.addSink(TomJonesBerney.init());
list.addSink(Contact.init());

}


This method calls each of the init() routine of each of the programs in turn, initialising the objects that are filled into "vp" above and filling in the text to "description". The initialised object is then added to a "ListSink", which is an array onto which objects can be placed, and which knows how to retrieve the object based on its name.

The "list" object is added to the central panel in the "West" position. The list object is programmed to delivery each of its contents as a list of hyperlinks. So simply by adding this object to the central "panel", this is the mechanism by which the menu items appear on the left hand side of the webpage.

At this stage, all the objects are poised, ready to deliver the html on request. But so far, nothing has yet been displayed.

The final piece of the code is showInfo(), which calls this method:

private void showInfo() {

SinkInfo requestedSink = list.find("Home")
show(requestedSink, false);

}

public void show(SinkInfo info, boolean affectHistory) {


// Generate an Instance of the SinkInfo
curSink = info.getInstance();
list.setSinkSelection(info.getName());
description.setHTML(info.getDescription());

// Display the new sink.
sinkContainer.add(curSink, DockPanel.CENTER);
sinkContainer.setCellWidth(curSink, "100%");
sinkContainer.setCellHeight(curSink, "100%");
sinkContainer.setCellVerticalAlignment(curSink, DockPanel.ALIGN_TOP);
curSink.onShow();

}

The routine showInfo() gets the SinkList object "list" to retrieve the program named "Home" (this is what the Home Page is called in the "Info.java" program) that was retrieved as a SinkInfo object and placed on the SinkList stack in the loadSinks() routine above. The method then calls show(requestedSink, false);.

The routine show() generates an instance of the Home Page SinkInfo program, from which it retrieves the name (eg. "Home") and the "Description" (ie. the text written in the Info.java program to the "Description" field of the SinkInfo object that it initialises and returns by the info.getInstance() command.

The routine then adds the object to the sinkContainer that was added in onModuleLoad() to the central "panel", to the right of the menu text. The code adds some formatting and positioning to sinkContainer. Since the program that responded to the call to initialise SinkInfo filled in all the Widgets it wanted with code containing the html, buttons etc it wanted to display, we now have the full website, primed to display the "Home Page" ready to roll as soon as the "compilation" is run and the resulting gbpAjax.html container page is opened.


The content programs

The content programs work in exactly the same way that the structure program above is described. They lay down a panel to which they add text, html, button or other widgets that are required to display.

Info.java

Each content program sets up a "Sink" object, which is initialised with a "SinkInfo" object.

public static SinkInfo init() {

return new SinkInfo("Home", "Home Page") {

public Sink createInstance() {

return new Info();

}

};

}

Earlier, a pointer to the "init()" method was added to the "list" SinkList. When the info.getInstance() method is called, it runs the "init()" method. This generates and returns a new SinkInfo instance which, in turn, creates an Instance of the program "Info", by callings is main initialisation routine (return new Info().

The initialisation of the "SinkInfo" instance provides "Name" (in this case "Home") and a "Description" (in this case "Home Page"). These parameters are simply saved to the appropriately named "Name" and "Description" variables of the "SinkInfo" object. The show() routine is programmed to extract the "description" and to display it as the first thing it does. (So if you want to avoid initial text, simply initialise the program with nothing in the "Description" field). Separately, the "Name" variable is what the "list" object extracts when it displays its list of hyperlinks. So this is where you define the text of the menu items, the hyperlinks that drive the webpage.

The text of the home page is delivered by the Info.java

The "Info()" method of the Info SinkInfo object defined by the Info.java program code delivers the text of the Home Page.


public Info() {

initWidget(new HTML(
"
This website is written using the Google Web Toolkit."
+ "

Various artist are featured, in groups of Photography, "
+ "Sculpture and Art. "
+ "Each of the pages is designed with a GWT Images page and "
+ "a sub-widget component of Image. We are still working on "
+ "implementing a TabPanel component, but have so far been unable to "
+ "associate an event listener for any tabbed page other the last.

",
true));

}

The task is so simple, that the program does not even bother with a panel - it just delivers a simple "HTML" widget containing the html text to display. The HTML widget comes from the GWT UI code in com.google.gwt.user.client.ui, and is accessed by the opening line import com.google.gwt.user.client.ui.HTML;. The HTML object is defined to be initialise with text that is stored in a variable. Since this is the object that is returned within the SinkInfo object, when GWT calls something like "object.toHTML", the text that is initialised in the code above is retrieved. So any text in the initialisation parameter above is displayed in the body of the webpage when the "Home" object (ie. the Home Page) is displayed.

NigelCohen.java

This program returns an object with a panel that displays an image and a "next" and "previous" button. It also returns an array of image locations as a variable, and code instructing GWT how to respond when someone clicks either of the buttons.

The standard initialisation routine is:

public static SinkInfo init() {

return new SinkInfo("Nigel Cohen", "All images are copyright Nigel Cohen."){

public Sink createInstance() {

return new NigelCohen();

}

};

}

The list of images is added to an object variable called "sImage":

private static final String[][] sImages = new String[][]{

new String[] {"Images/dsc_1484.jpg", "Burnham Beeches, Summer 2006"},
new String[] {"Images/dsc_1499.jpg", "Sunbathing, Summer 2006"},
new String[] {"Images/dsc_1513.jpg", "Water Lilly"},
new String[] {"Images/dsc_0911.jpg", "Amersham, UK"},
....

};

There are further variables that different methods within the object need to access. So these are also defined at within the object in a way that they are visible even in the sub-routines.

private int curImage;
private Image imageCentral = new Image();
private HTML htmlTitleCentral = new HTML("", true);

private Image loadingImage = new Image("Images/blanksearching.gif");
private Image nextButton = new Image("Images/forward.gif");
private Image prevButton = new Image("Images/back.gif");

The main work is done when the routine is initialised:

public NigelCohen() {


// Add the programs capability to respond to button clicks
nextButton.addClickListener(this);
prevButton.addClickListener(this);

// Create a panel onto to which the buttons are placed
DockPanel topPanel = new DockPanel();
topPanel.setVerticalAlignment(DockPanel.ALIGN_MIDDLE);
topPanel.add(prevButton, DockPanel.WEST);
topPanel.add(nextButton, DockPanel.EAST);

// Create the central "VerticalPanel", and place on it
// the Image Title, the Buttons panel and the Image widgets
VerticalPanel panel = new VerticalPanel();
panel.add( htmlTitleCentral );
panel.add(topPanel);
panel.add(imageCentral);

// Initialise the composite "VerticalPanel" widget
initWidget(panel);

// Load the first image on the stack (ie. index of 0)
loadImage(0);

}

This routine creates a main panel, onto which it places an HTML widget to display the title of the Image being displayed, the navigation buttons and the Image widget itself.

The initWidget(panel) code is a little curious. The VerticalPanel descends from a "Composite" object. GWT needs a single "parent" Widget from which to extract the html/javascript. If there are several Widgets, you need to identify the parent Widget, to which each of the other widgets will be attached. This is achieved with the initWidget() command, by setting the parent's reference to itself. If you call the initWidget() command after you have already called it, GWT throws out an exception that is only visible at the time the web page is displayed. This is an example of why code that can give you feedback of javascript errors in the browser is invaluable.

Another important aspect of this code is the ability to respond when a user clicks the "next" of "previous" button. The idea is that the code will look at the stack of images, extract the next or previous image and display it. The way you add the ability to handle user response it to add a "listener" object. The "listener" object is added to each widget that you want to respond to an action. In this case, we are adding the object to the "next" and "previous" buttons. The listener object can be added in either of two ways. The above technique simple calls the "addClickListener" method of its widget. This assumes that there is a routine in the object called "onClick()" which provides the functionality required. If this routine is missing, you will experience a "compile" error, where GWT feeds back the error at compile time. An alternate would have been to set up a "Listener" object with the required functionality coded in the input parameter.

In this case, the onClick routine looks like this:

public void onClick(Widget sender) {

if (sender == prevButton) {

loadImage(curImage - 1);

} else if (sender == nextButton) {

loadImage(curImage + 1);

}

}

The loadImage routine looks like this:

private void loadImage(int index) {

if (index < 0) {

index = sImages.length - 1;

} else if (index > sImages.length - 1) {

index = 0;

}

curImage = index;
htmlTitleCentral.setHTML("

" + sImages[curImage][1] + "

");
imageCentral.setUrl(sImages[curImage][0]);

}

In this routine, there is simple logic to tell the stack to go back to the beginning if the user clicks past the end, or to the end if the user clicks back past the beginning. In the GWT objects, the image is "displayed" simply by update the image's URL variable, which is extracted from the sImages array, and we set the HTML object's text with the image title derived also from the sImages array, simply by calling its "setHTML()" method.


Formatting and style

GWT have provided a wonderfully simple way to format widgets and add style to webpages. By adding a simple stylesheet, you can define the display properties of just about every widget. You can also add standard "class" attributes to any html you generate to make use of the css coding.

GWT have used a very sensible naming protocol which it attaches to each Widget. The convention allocates each widget with a class along the lines of "gwt-[Widget]" (eg. .gwt.TextBox), which you have full control over in the .css file you define in your base "html" file.

See The stylesheet gbpAjax.css as an example.


Concluding

So there we have it.

The examples provided by Google give significantly expanded capabilities. With an understanding of the concepts behind GWT, exploration of the capabilities should be far more functional and much more fun.