Sunday, February 27, 2011

ClientBundle, UIBinder and CSS (GWT)

In a previous post I was discussing all the possible ways for using image resources with GWT. Another thing you might want to do is dealing with CSS.

1) Standard use of CSS. While using GWT you can certainly use the CSS how you would use them for any other application, simply declaring CSS classes in a *.css file and importing it in the webpage of interest. With GWT widgets you will then simply set the style name as follows:
widget.setStyleName("cssClassName");
This approach works, however, if the CSS declaration is missing no exceptions are raised. Also, if you use multiple CSS files as I do, it is always annoying to find the declarations when you need to.

2) Using the UIBinder. If you are already using UIBinder, the easiest way to include CSS declarations is to add them to the binder. It is easy and it is safe as the Eclipse plugin is helping you out in finding missing declarations. I always use Eclipse but if you don't I am assuming you'll still find the problems at compile time.
<ui:UiBinder
  xmlns:ui='urn:ui:com.google.gwt.uibinder'
  xmlns:g='urn:import:com.google.gwt.user.client.ui'>

    <ui:style>
       .outer {
          width: 100%;
       }
    </ui:style>

    <g:VerticalPanel styleName='{style.outer}'>
    </g:VerticalPanel>
</ui:UiBinder>
The downside of this approach is redundancy. Sometimes I want to use CSS declarations multiple times and, with this approach, I have to repeat them for each single Binder.

3) Using CssResource. Another alternative consists in doing something similar to what you can do for icons with ImageResource. First I declare the set of declarations of the stylesheet in a file that I name Commons.css:
.smallIcon {
    height: 16px;
    width: 16px;
}

Then, linking the CSS file, I declare the use of the stylesheet as a resource for the application:
public class Example implements EntryPoint {

  public interface Resources extends ClientBundle
  {
     public static final Resources INSTANCE =  GWT.create(Resources.class);
 
     public interface Resources extends ClientBundle { 
        @Source("org/example/application/client/Commons.css")
        CommonsCss commonsCss();
     }
     
     ...
  }
  
}
Now, where the dots in the above snippet are, I can declare the stylesheet declarations I want to expose to the application:
public interface CommonsCss extends CssResource {
     String smallIcon();
}
As the CSS class is named as the method everything works fine. However, sometimes you might want to change the name of the method. Using Java annotations you address that issue as well:
public interface CommonsCss extends CssResource {
     @ClassName("smallIcon")
     String smallIconClass();
}
Now, we can write something like:
Resources resources = Resources.INSTANCE.factory().create();
Image img = new Image();
img.setStyleName(resource.commonsCss.smallIcon());
...
This approach allows you to collect in one single place CSS declarations you need to use in multiple packages in your application. Also you can leverage a good amount of validation in your Java code. You might argue the process can be a bit tedious but I can assure you, for a big GWT application, it can help you saving lots of time later on especially when refactoring the code.

There are other interesting things to know about the ClientBundles, but for now I'll stop here.

No comments: