Software engineer, working at ArcBees. A heavy GWT / Java / Spring developer. Mrabti is a DZone MVB and is not an employee of DZone and has posted 3 posts at DZone. You can read more from them at their website. View Full User Profile

Writing Custom CellRenderer using the Declarative new GWT 2.5 UiRenderer

12.05.2012
| 4663 views |
  • submit to reddit

Before GWT 2.5, writing a custom CellRenderer to present data in a certain manner was very complicated and very difficult to achieve.

All the custom UI code had to be written inside a Java class that inherits from the AbstractCell class, using HTML strings concatenation, which is a pretty clunky solution. If you want something cleaner you could choose to use the @Template class, but still it is very difficult to write and to maintain.

I always said, if only Google would add in GWT the ability to define the UI of a custom CellRenderer using XML, just like they did with the introduction of UiBinder, and to have the possibility to bind the UI components using the @UiField and to hook event processing using @UiHandler ==> Hooraaahhhh !!!! This dream came true with GWT 2.5, and it is called the UiRenderer.

In this article I’ll show some snippets of codes of a real use case example to explore most of the features brought to us with UiRender. First let’s describe the custom CellRenderer we will implement.

Image

As you can see in the schema, the cell item renderer is composed of a picture, a text and two buttons, one to remove the item and the other one to open a popup for editing data.

To create a custom CellRenderer using UiRenderer we have to create two files, one containing the XML declarative UI code (think of the MyView.ui.xml files), and the other one holds the associated Java view (think of the MyView.java). The two files must have the same name.

Declarative UI code of MyCellRenderer.ui.xml :

One thing to remember, the difference between the UiRenderer and UiBinder is that in UiRenderer you can only use HTML tags. All GWT custom components will not work in here. I hope Google will add this feature in a future release.

<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'>
    <ui:with field='name' type='java.lang.String'/>
    <ui:with field='image' type='java.lang.String'/>

    <ui:style>
        .imageWrapper {
            float: left;
            margin-right: 10px;
        }
        .imageWrapper img {
            width: 64px;
            height: 64px;
        }
        .infoWrapper {
            float: left;
        }
        .infoWrapper div span {
            display: inline-block;
            marin-right: 5px;
            text-decoration: underline;
            cursor: pointer;
        }
    </ui:style>

    <div>
        <div class="imageWrapper">
            <img alt="{name}" src="{image}" />
        </div>

        <div class="infoWrapper">
            <h3><ui:text from="{name}"/></h3>
            <div>
                <span ui:field="remove">Remove</span>
                <span ui:field="update">Update</span>
            </div>
        </div>

        <div style="clear: both;"/>
    </div>
</ui:UiBinder>

Custom CellRenderer Java file MyCellRenderer.java :

This file will manage the UI logic, like for example passing the data to be displayed, formatting data (Date conversion, number conversion…), handling events…

In this example, we have two events to handle (the click on Remove and Update buttons). Thanks to UiRenderer, we can use ui:fields and the @UiHandler annotation. There is some other boilerplate code to add, like the onBrowse event handler to make it work (everything is well described in the snippet of code below).

public class MyCellRenderer extends AbstractCell {
    // This can be compared to UiBinder --> here where all magic is done
    public interface Renderer extends UiRenderer {
        void render(SafeHtmlBuilder sb, String name, String image);

        void onBrowserEvent(MyCellRenderer o, NativeEvent e, Element p, Person n);
    }

    private final Renderer uiRenderer;

    @Inject
    public MyCellRenderer(final Renderer uiRenderer) {
        super(BrowserEvents.CLICK);
        this.uiRenderer = uiRenderer;
    }

    @Override
    public void onBrowserEvent(Context context, Element parent, Person value, NativeEvent event,
                               ValueUpdater valueUpdater) {
        uiRenderer.onBrowserEvent(this, event, parent, value);
    }

    @Override
    public void render(Context context, Person value, SafeHtmlBuilder safeHtmlBuilder) {
        // All data extraction, transformation should be done in here
        String name = value.getFirstName() + value.getLastName();
        String image = value.getImage();

        // We directly the uiRenderer and we pass the HtmlBuilder
        uiRenderer.render(safeHtmlBuilder, name, image);
    }

    @UiHandler({"remove"})
    void onRemovePersonClicked(ClickEvent event, Element parent, Person value) {
        Window.alert("Do you want to remove : " + value.getFirstName());
    }

    @UiHandler({"update"})
    void onUpdatePersonClicked(ClickEvent event, Element parent, Person value) {
        //Maybe use the ActionCell.Delegate to process the action elsewhere...
        Window.alert("Do you want to update : " + value.getFirstName());
    }
}

Using the new Renderer in a CellList :

Now that we are done writing our custom, sophisticated and good looking CellRenderer (:D), we can use it the same way we used the old legacy Renderer. I added below a small snippet to show you how in case you forget:

public class MyView extends Composite {
    public interface Binder extends UiBinder<Widget, WebCartView> {
    }

    @UiField(provided = true)
    CellList personList;

    @Inject
    public MyView(final Binder uiBinder, final MyCellRenderer myCellRenderer) {
        personList = new CellList(myCellRenderer); // Same way as using old Renderers
        ....  // Continue what you always do...
    }
}

That’s it for this article, I hope this was useful and will help you build more awesome sophisticated apps without having to write a lot of complicated code. PS: this new UiRenderer can be used with any Cell based GWT widget.

For more details check the following links : http://goo.gl/2fePf (GWT 2.5 UiRenderer official docs ), http://goo.gl/ogR3J (Cell widget docs).


Published at DZone with permission of Mrabti Idriss, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)