FAQ - UI
- Layout
- Event Handling and Performance
- How do I display a big list of items in a table with good performance?
- What can I do to make images and borders appear to load more quickly the first time they are used?
- Why do I see poor performance when using a one pixel tall background image?
- As the application grows, event handlers seem to fire more slowly
- How can I efficiently handle events from many interior Widgets?
Layout
I’m having trouble with my layout. How do I tell where my panels are?
When you are working with complex layouts, it is sometimes difficult to understand why your widgets aren’t showing up where you think they should.
As an example, suppose you were trying to create a layout with two buttons, one on the left side of the browser, and the other on the right side of the browser. Here’s some code that attempts to do that:
VerticalPanel vertPanel = new VerticalPanel();
DockPanel dockPanel = new DockPanel();
dockPanel.setWidth("100%");
dockPanel.add(new Button("leftButton"), DockPanel.WEST);
dockPanel.add(new Button("rightButton"), DockPanel.EAST);
vertPanel.add(dockPanel);
RootPanel.get().add(vertPanel);
And here is how the screen ends up:
What went wrong? We set the Dock Panel to fill up 100% of the space, and then each button to the left or right of the screen. At this point, it can be helpful to probe and see what the boundaries of your widgets are, because they may not be where you think.
The first technique is to use a DOM inspection tool, such as Firebug for the Firefox browser. As you hover over elements in the DOM they will highlight in the browser window.
Another technique you can use is to modify your GWT code to turn on borders on your panels (or other widgets.)
VerticalPanel vertPanel = new VerticalPanel();
DockPanel dockPanel = new DockPanel();
dockPanel.setWidth("100%");
dockPanel.add(new Button("leftButton"), DockPanel.WEST);
dockPanel.add(new Button("rightButton"), DockPanel.EAST);
dockPanel.setStylePrimaryName("dockPanel");
dockPanel.setBorderWidth(5);
vertPanel.add(dockPanel);
vertPanel.setStylePrimaryName("vertPanel");
vertPanel.setBorderWidth(5);
RootPanel.get().add(vertPanel);
You can also use the associated CSS stylesheet to change the border properties. In this case, the stylesheet changes the colors of the borders to make them easier to tell apart:
.dockPanel {
border-color: orange;
}
.vertPanel {
border-color: blue;
}
Now you can see clearly where the outlines of each panel are:
In this case, the outermost panel needs to be 100% width as well, so we change the code, hit Refresh in the browser window:
vertPanel.setWidth("100%");
This is closer to what we want, but it still isn’t right. Now we need to change the cell alignment in the DockPanel instance:
Button rightButton = new Button("rightButton");
dockPanel.add(rightButton, DockPanel.EAST);
dockPanel.setCellHorizontalAlignment(rightButton, HasHorizontalAlignment.ALIGN_RIGHT);
(Don’t forget to turn off these styles when you are finished!)
How do I create an app that fills the page vertically when the browser window resizes?
As of GWT 2.0, creating an application that fills the browser is easy using Layout Panels. LayoutPanels such as DockLayoutPanel and SplitLayoutPanel automatically resize to the size of the window when the browser resizes.
The Mail sample application demonstrates SplitLayoutPanel in action.
See the Developer’s Guide on Layout Panels for more details and code snippets.
Why doesn’t setting a widget’s height as a percentage work?
In some cases, the Widget.setHeight() and Widget.setSize() methods do not seem to work with sizes expressed as percentages. The GWT widgets do not do any calculations with these values. These methods set the widget’s CSS height and/or and width properties and depend on the behavior of the enclosing widget.
In standards mode, if an element (such as a <
div>
) is inside a table cell <
td>
, you cannot set the <
div>
height and width attributes as percentages. They are ignored. For example, the styles in the <
div>
element below is ignored in standards mode:
<td>
<div style="height:100%;width:100%;>test</div>
</td>
Workarounds include
- Try changing the enclosing outer widget to a different type. For example, VerticalPanel is implemented with an HTML
<
table>
element and might behave differently than AbsolutePanel which is implemented with an HTML<
div>
element. - If the
<
DOCTYPE…>
declaration in the HTML host page is set to standards mode (such as HTML 4.01 Transitional), try removing the<
DOCTYPE …>
declaration or changing it to put the browser rendering engine into quirks mode.
Does GWT support standards mode?
As of GWT 1.5, yes. Standards mode is supported. For backwards compatibility, quirks mode is also still supported. Support for quirks mode, however, may be dropped in future versions of GWT, and it is recommended that you update your GWT-enabled pages to use standards mode.
Event Handling and Performance
How do I display a big list of items in a table with good performance?
Here are a few pointers for addressing the need to display lots of data through a table widget efficiently.
Fixed width tables
Performance wise, you are better off using a fixed-width table than one that dynamically resizes.
RPC and other network transaction considerations
Most applications are loading the data for their table over the network. Try to reduce the number of round trips your application has to make to populate the table. Two main strategies come to mind:
- Fetch multiple rows in the same request.
- Combine activity from the different subsystems of the app into a single RPC call. From the combined data structure so you can fetch data relevant to different parts of the application in the same RPC call and reduce user wait time.
Testing for Performance
If you have a large table, make sure you run a test with the largest number of rows you want to support. On some browsers it has been observed that table performance slows worse than linearly with respect to the number of rows.
Try using PagingScrollTable
If you find yourself with a table performance issue, the best way to address it might be not display most of your data. A table implementation called the PagingScrollTable can help you display data a page at a time instead of having to wait for the entire dataset to download to the browser. This breed of table is very common in web apps and won’t be foreign to users. As a rule of thumb, consider using PagingScrollTable if you have more than 50 rows of data to display.
Note that the PagingScrollTable is a part of the GWT Incubator project which you will need to download separately from the core GWT distribution.
If you can use the PagingScrollTable, then consider the CachedTableModel (also a part of the GWT Incubator) you can use to automatically prefetch rows, such as the next page. This can increase the perceived speed considerably.
Try using the BulkTableRenderer and PreloadedTable classes
A BulkTableRenderer will render all rows in a table at once. BulkTableRenderer has derived types for different purposes. As long as your table contents are not widgets, you provide a table model and the BulkRenderer creates the entire table rendered as a single HTML string and set with setInnerHtml() which can be 2-10x faster.
Note that after being loaded, widgets, cell spans, row spans etc. may be added to the table, but there will be no speed advantage for them.
What can I do to make images and borders appear to load more quickly the first time they are used?
The first time your application accesses an image, you may notice that it may only partially load, or load more slowly than it displays on subsequent invocations. Since first impressions are important, you may want to try this trick.
The underlying issue is that the images needed for the background, border, or animation are fetched from the server on demand. A solution is to pre-fetch the images so that they are sitting in the browser cache by the time the animation is actually invoked. Go through your CSS files for the images used in your styling that are running slowly, and use the Image.prefetch() method.
Image.prefetch("images/corner-bl.png");
Image.prefetch("images/corner-br.png");
Image.prefetch("images/corner-tl.png");
Image.prefetch("images/corner-tr.png");
Why do I see poor performance when using a one pixel tall background image?
It is common to use a one pixel image for a repeating background image. Be advised, however, that there can be a performance penalty for covering a large area with a small image (in particular, we noticed the problem on Internet Explorer). This is particularly noticeable when using GWT animations.
One way to work around the problem is to increase the size of your image so that the rendering image doesn’t have to repeat the image as often. So, instead of using a 20 x 1 sized image, you might try a 20 x 100 image. The tradeoff here is that the browser will have to download a larger image over the network.
As the application grows, event handlers seem to fire more slowly
If you are creating an interface with many widgets that need to respond to events, such as click
events, you may find that the application handles events more and more slowly. One example of this might be having a list of widgets in a table.
One contributor to poor performance can be the overhead of managing many click handler instances. Consider a common way of handling click events:
Button newButton = new Button(titleText);
newButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
dialogBox.hide();
}
});
Each widget has its own click handler instance created by adding an anonymous inner class when the widget is created. This construct is convenient and causes no performance issues for most cases, but when creating many widgets that need event handling, consider creating a single click handler and sharing it between multiple widgets:
class MyApp implements EntryPoint, ClickHandler {
/* Be careful with saving UI components in datastructures like this:
* if you remove a button from the app, make sure you also remove
* its reference from buttonMap HashMap to avoid memory leaks.
*/
Map buttonMap<Button, Integer> = new HashMap<Button,Integer>();
public void onModuleLoad() {
FlowPanel panel = new FlowPanel();
for (int i = 1; i < 100; ++i) {
Button newButton = new Button("Option " + i);
newButton.addClickHandler(this);
panel.add(newButton);
buttonmap.add(newButton, Integer.valueOf(i));
}
RootPanel.get().add(panel);
}
// The shared ClickHandler code.
public void onClick(ClickEvent event) {
Object sender = event.getSource();
if (sender instanceof Button) {
Button b = (Button) sender;
Integer context = buttonMap.get(b);
if (context != null) {
// ... Handle the button click for this button.
}
}
}
}
How can I efficiently handle events from many interior Widgets?
Quirksmode has a writeup of the background behind Event Bubbling in how different browsers deliver events. When using a GWT event handler, GWT hides all this away from you and delivers events only for the element you are interested in. This is a useful programming abstraction, but can lead to slow performance when many handlers are added to the system.
If you are building a widget, you may want to consider handling events in a different way, called Event Bubbling. Essentially, this technique means you have one handler for the entire widget, and you have one handler callback that executes on behalf of all interior widgets.
See the implementation of the Tree class for an example of using Event Bubbling in GWT.