Author: Renat Akhmerov
This example is a live code that demonstrates the FastContainer pattern described in our article “GWT: improving performance”. The goal of the example is to research setInnerHtml phenomenon more detailed and to see concrete results in milliseconds. Besides that I am going to consider a few important aspects of the real implementation of FastContainer pattern. You can see working example at http://www.stravati.com/examples/fastcontainer and download its source code from http://www.stravati.com/examples/sources/fast-container-demo-src.zip.
The page of the example contains two sections, left and right, where test components are displayed. To draw components in the left section we’ve used FlexTable as a container which includes other small controls, in other words, this is a standard approach widely used in GWT development. Actually, this type of component has been borrowed from our last project where it represented some task needs to be completed. So in a simple case it has a checkbox (completed or not), a title (description) of the task, and two dates, kick off and do by. The picture below shows that type of component.
![]() |
In the right section each component is an implementation of FastContainer pattern. At the top of the page there is a special check box that tells a program to include controls into the component or not. To make it all clearer let’s dig into the code.
A standard component, as I said before, implemented with FlexTable class. Its code is pretty straightforward. The following listing shows two methods creating the layout for the component:
private FlexTable getFrame() { if (frame == null) { frame = new FlexTable(); frame.setStyleName("item"); frame.setCellSpacing(0); frame.setWidget(1, 1, getLayout()); frame.setWidget(2, 2, new Label("")); FlexCellFormatter formatter = frame.getFlexCellFormatter(); formatter.setStyleName(0, 0, "item-leftTop"); formatter.setStyleName(0, 1, "item-top"); formatter.setStyleName(0, 2, "item-rightTop"); formatter.setStyleName(1, 0, "item-leftMiddle"); formatter.setStyleName(1, 1, "item-contentCell"); formatter.setStyleName(1, 2, "item-rightMiddle"); formatter.setStyleName(2, 0, "item-leftBottom"); formatter.setStyleName(2, 1, "item-bottom"); formatter.setStyleName(2, 2, "item-rightBottom"); } return frame; } private FlexTable getLayout() { if (layout == null) { layout = new FlexTable(); layout.setWidth("100%"); layout.setCellSpacing(0); layout.setWidget(0, 2, getKickOffLabel()); layout.setWidget(1, 2, getDoByLabel()); } return layout; }
Rounded corners for the component is created as background images for table cells in respective css classes. You can just take a look at the source code to see the complete style sheets. And here is the one of the methods which allows setting control into the component:
public void setTitleWidget(Widget widget) { getLayout().setWidget(0, 1, widget); }
This method, in fact, describes where the title widget will be inside the container. There are also methods for setting other controls (checkbox, kick off and do by values). In our simple application such straightforward components are instantiated and configured by entry-point class FCEntryPoint the way like the following:
StandardComponent aComponent = new StandardComponent(); Label title = new Label("Some title here"); title.setStyleName("item-title-label"); aComponent.setTitleWidget(title); aComponent.setCheckboxWidget(new CheckBox()); aComponent.setKickOffWidget(new Label("Today")); aComponent.setDoByWidget(new Label("Tomorrow"));
Of course, there will be more sophisticated code related with component’s configuration (event handlers, styles, focus management etc.) and most likely all this code will be encapsulated inside a class which will provide an interface to work with the component using high-level terms such as Date value against Label instance, boolean value against Checkbox, and so forth. But in this case we aimed to create just a simple application focusing on FastContainer pattern implementation not overloading the code with additional classes.
There is much more interesting things related with components built with FastContainer class that plays the key role in a life cycle of a component. There is no huge amount of code in this class and I’ll cite it here as is:
public class FastContainer extends Widget { private Context context; public FastContainer(Pattern pattern) { this.context = new Context(); Element creatorElement = DOM.createDiv(); DOM.appendChild(RootPanel.getBodyElement(), creatorElement); DOM.setInnerHTML(creatorElement, pattern.generatePattern(context)); Element elem = DOM.getFirstChild(creatorElement); setElement(elem); } public void setWidget(String fieldName, Widget widget) { Parameter param = context.getParameter(fieldName); if (param == null) { throw new RuntimeException("There is no such parameter in context!"); } Element element = DOM.getElementById(param.getId()); if (element != null) { DOM.appendChild(element, widget.getElement()); DOM.setEventListener(widget.getElement(), widget); } } }
To have the possibility to work with plain html source of the component FastContainer is injected with Pattern. The last one is responsible for generating html source code and creating a set of template parameters encapsulated in the class Context. If you look at the code carefully you’ll see pretty sophisticated code that initializes the DOM-element of the component. Well, if we do this straightforward setting inner html right into element of the component then the call DOM.getElementById(param.getId()) doesn’t work (it always returns null) due to unknown reason. So we have to use a trick like this to make it possible to attach source html code correctly. It’s also worth commenting the last row in the code snippet above (DOM.setEventListener(widget.getElement(), widget)). This call is required to repair all event handlers have been set for a widget. All GWT panels actually do the same otherwise newly attached widgets lose their event handlers. Now let’s look at the ItemPattern code to understand its role in the pattern.
Class ItemPattern implements the interface Pattern where just one method is described. This method is:
String generatePattern(Context context);
You don’t need to be a genius to guess that this method’s implementations should return a source html code of the component. Just one interesting aspect: parameter context. The main class FastContainer entrusts to the Pattern a job to create a set of parameters related to the html source code. Each of these parameters should be considered as a way of accessing to some element in DOM.
public class ItemPattern implements Pattern { public String generatePattern(Context context) { StringBuffer html = new StringBuffer(); html.append("<table class="item" cellspacing="0">"); html.append("<tbody><tr>"); html.append("<td class="item-leftTop"> </td>"); html.append("<td class="item-top"> </td>"); html.append("<td class="item-rightTop"> </td>"); html.append("</tr>"); html.append("<tr>"); html.append("<td class="item-leftMiddle"> </td>"); html.append("<td class="item-contentCell">"); int id = Parameter.nextId(); Parameter checkBoxParam = new Parameter("checkBox", id); Parameter titleParam = new Parameter("title", id); Parameter kickOffParam = new Parameter("kickOff", id); Parameter doByParam = new Parameter("doBy", id); context.addParameter(checkBoxParam); context.addParameter(titleParam); context.addParameter(kickOffParam); context.addParameter(doByParam); html.append("<table cellspacing="0">"); html.append("<tbody><tr>"); html.append("<td> </td>");
This chunk of the code from the class ItemPattern shows the essential from the generating source html process. The html itself is generated using StringBuffer class supported by GWT. Each element in the html (it becomes a DOM-element after attaching) to be injected with some widget is associated with respective Parameter instance which receives a name of the element (a value needed to access the element using method setWidget(String fieldName, Widget widget) in FastContainer) and a number value that should be unique for the FastContainer instance. In our example this is implemented via static method nextId in Parameter class but in a real application there might be smarter algorithm. The important thing to note is that Parameter generates a unique identifier for the field. For example, if we have Parameter instantiated like new Parameter(“title”, 12) then the method getId() will return “title_12” that is a unique value for this field in DOM. The number 12 here is a sort of FastContainer instance number. So when we call getParameter(“title”) of Context instance we get this unique identifier.
This was all important aspects of FastContainer pattern implementation. You can also see the complete source code of the example to dig in all the details.
Below the table of the test results for three popular browsers is shown.
browser | standard, ms | fast container, ms | ||
empty items | with controls | empty items | with controls | |
Firefox 2.0.0.12 (Windows) | 730 | 1300 | 265 | 935 |
Internet Explorer 6 | 702 | 1100 | 171 | 686 |
Safari 3.0.3 (Windows) | 280 | 436 | 94 | 250 |
All of the results have been got on the same machine. I won’t describe the parameters of that machine because our goal now is to show the difference between the two techniques of building components in GWT. Looking at the results we can see that the time spent for embedded controls creating and attaching is almost the same for both cases standard and using FastContainer. This are: 570ms and 670ms for Firefox, 398ms and 515ms for IE 6, 156ms and 156ms for Safari. The little difference in ~100ms can be explained by using DOM.getElementById(param.getId()) in FastContainer implementation to search target element to attach a widget to. We can guess this method works as slower as more elements we have in DOM. So there might be ways to improve this say by searching target element starting with component’s element itself. In other words, only within component’s scope. What’s really interesting in the results in the table above is that empty components (just frames without any embedded controls) are rendered a few times faster! Especially, this difference is significant for IE 6 (4.1 times). This result means that if we have, for example, some type of components with more sophisticated internal structure that can be implemented with FastContainer pattern (as an html code) then this difference will be even bigger than in our example because, to be honest, here the components are pretty simple.
The results of this simple test showed performance benefits of using setInnerHtml for building GWT components. As we already said before, rendering speed can be essentially increased by using this approach. FastContainer pattern is one of the concrete techniques for building components based on this phenomenon that we can offer to use in real applications development. Of course, it is possible to use setInnerHtml as is to build something you may want, but talking about the pattern we emphasize that it is exactly a container supposed to have an ability to contain GWT widgets inside. It also allows accessing these widgets during the component’s life.
In summary I’d like to say that the idea of FastContainer is not a completely new. There are already such things as HTMLTemplateWidget that is for constructing widgets from html source code. The GWT team also announced their GWT Declarative UI for the further releases. So we’re looking forward to play with that stuff. The goal of this example and the related articles was to concentrate on performance benefits and to offer working technical decision that could be already used in GWT development.
Do you like the article and want us to develop your project? Feel free to contact us here!
This entry was posted on Wednesday, March 19th, 2008 at 9:55 am and is filed under gwt.