Pages

Monday, May 7, 2012

GWT - FlowPanel + CSS vs HorizontalPanel and VerticalPanel

I've been working with GWT for the past half year, and am extremely excited about this awesome web-framework !
I recommend anyone who is doing web-development to look into it. As a person that came from the world of Java Swing, the transition was very smooth, and most importantly - fun !

Anyhow, this is not the focus of this particular post. I'm not here to say how great GWT is or
persuade you to use it (although you really should if you want to create amazing rich web apps!!!).

This is about GWT's particular widget - FlowPanel and its advantages over using HorizontalPanel & VerticalPanel.

The "problem" is as follows:
Using HorizontalPanel or VerticalPanel to layout your widgets can be very intuitive and comfortable, especially if you come from the world of Java Swing, where Layout Manager extending components help you to lay out your widgets, without forcing you to think too much how they do it.
GWT widgets are a little different.
GWT is a compiler in it's core, that takes Java code, and compiles it into JavaScript that eventually runs on your browser. Different widgets compile into different HTML elements that are attached into the DOM and need to be rendered, so it is very important to keep your DOM as compact and simple as possible so the browser has to deal with less elements thus rendering your page faster, and is also helpful when you need to look yourself at the DOM to find a specific element or understand the hierarchy.

Using the HorizontalPanel or VerticalPanel, can create some serious performance overhead, and especially when you have lots of those in your application.
The reason is because GWT compiles HorizontalPanel & VerticalPanel into HTML Tables.
When you have a lot of those tables, and with many widgets inside you will end up having
lots of those "<td></td>" , "<tr></tr>" tags which resemble the lines and cells of the tables,
and of course the encapsulating tags of the "<table></table>" & "<tbody></tbody>".

FlowPanel on the other hand is compiled into a "<div></div>" - plain and short.

So as you can see, the problem is that when you have lots of those Vertical/HorizontalPanel - you end up generating a lot more elements in the DOM.

This in turn reflects on the performance of your application. Each time you'd want to manipulate those tables - add a cell, remove one, change content, the look-up into the DOM is longer, and the operations consume more processing.
After the change you will end up making to the DOM, the browser will have to re-render those changes in order to reflect them to the user - the more complicated hierarchy you will have the slower the rendering will be performed.

Ok, now I feel I repeated myself enough, so the point should be clear.

So, after understanding why it's better to use (in many cases, although not necessarily all), here's what needs to be done in order to make the change:


  1. Replace your VerticalPanel (or HorizontalPanel) widget with FlowPanel widget.
  2. In case you're using HorizontalPanel and wish to place widgets next to each other, don't forget to use CSS float attribute.

Example:

import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;


public class MessagePanel extends Composite {


// Using FlowPanel instead of HorizontalPanel
private final FlowPanel contentPanel;


// Widgets of the message panel.
private Label messageCaption ;
private TextBox messageContent ;
private Button send ;

/**
* C'tor
*/
public MessagePanel() {
this.contentPanel = new FlowPanel();
initWidget(this.contentPanel) ;
// Lets add contents to the panel.
addContents();
// add styles !
addStyles() ;
}


private void addContents() {
// add a name caption
messageCaption = new Label("Message:");
contentPanel.add(messageCaption);
// add an input text field
messageContent = new TextBox();
contentPanel.add(messageContent);
// add a "send" button...
send = new Button("Send message!");
contentPanel.add(send);
}

private void addStyles() {
messageCaption.getElement().getStyle().setProperty("float", "left") ;
messageContent.getElement().getStyle().setProperty("float", "left") ;
send.getElement().getStyle().setProperty("float", "left") ;
}
}


Of course, the code above is only to demonstrate what you can achieve, but not necessarily
how you should really implement it. CSS styles should be put in a CSS file and you should follow GWT's best practices. 

In any case, the outcome of instantiating this widget and adding it results the following:



And results in the following hierarchy in the DOM (from Chrome) :

<div>
<div class="gwt-Label" style="float: left; ">Message:</div>
<input type="text" class="gwt-TextBox" style="float: left; " id="acpro_inp0">
<button type="button" class="gwt-Button" style="float: left; ">Send message!</button>
</div>

So, as you can see this part of the DOM is very concise and straightforward.

Change to FlowPanel to boost your application's performance & to generate simpler DOM.

Happy tuning !