Search code examples
javagwtuibinder

How to use GWT UI Binders in "external" Package


I'm working on a web app using GWT, and am in the process of modifying it to be built using UiBinders.

I have a working version of several binders contained in the same package as the classes that reference them, but a lot of these have universal utility across the entire app, and I was hoping to factor them out into a uibinders package.

Here are some snippets of the working code

The TableListPanel.ui.xml file:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:HTMLPanel>
<div id="tableListPanel">
    <g:HTMLPanel styleName="buttonPanel" ui:field="buttonPanel"></g:HTMLPanel>
    <g:HTMLPanel styleName="tableList" ui:field="tableList"></g:HTMLPanel>
    <table align="center"><tr>
        <td align="center"><g:HTMLPanel styleName="tableMenu" ui:field="tableMenu"></g:HTMLPanel></td>
    </tr></table>
</div>
</g:HTMLPanel>

</ui:UiBinder> 

The TableListPanelBinder java file:

public class TableListPanelBinder extends Composite implements HasText {

private static AuditPanelUiBinder uiBinder = GWT
        .create(AuditPanelUiBinder.class);

interface AuditPanelUiBinder extends UiBinder<Widget, TableListPanelBinder> {
}

public TableListPanelBinder() {
    initWidget(uiBinder.createAndBindUi(this));
}

@UiField
protected HTMLPanel buttonPanel;

@UiField
protected HTMLPanel tableList;

@UiField
protected HTMLPanel tableMenu;

public void bindUiElement(HTMLPanel container, Widget widjetObj) {
    container.add(widjetObj);
}

And finally the call in the code that binds widgets to the above ui:fields :

public AuditReportProfilesPanel() {
    super();
    this.setWidth("100%");
    this.setSpacing(4);


    this.parmSetsTable = createParmSetsTable();
    SimplePager.Resources pagerResources = GWT.create(SimplePager.Resources.class);
    pager = new SimplePager(TextLocation.CENTER, pagerResources, false, 0, true);
    pager.setDisplay(parmSetsTable);

    TableListPanelBinder reportListBinder = new TableListPanelBinder();
    this.add(reportListBinder);

    // -START- Bind UI Elements -START-
    reportListBinder.bindUiElement(reportListBinder.tableList, parmSetsTable);
    reportListBinder.bindUiElement(reportListBinder.tableMenu, pager);
    reportListBinder.bindUiElement(reportListBinder.buttonPanel, createActionBar());
    //-END- Bind UI Elements -END-
}

I know this is probably not the cleanest way to set this up, but I'm learning as I go.

I know moving the Binder to a different package means that the private/protected access modifiers will prevent me from reaching the code, but changing them to public doesn't do me any good either.

Any suggestions to a fix would be much appreciated. This app has several different class packages (That essentially equate to different pages in the app), and they all use a similar table setup, so I would like to avoid having duplicate code in every package; I'm just not 100% sure on the best way to achieve that.

Would creating Abstract classes in the Binders package and then extending them in the individual app packages solve the problem, or is there something I'm missing?


Solution

    1. Write your generic UiBinders and corresonding classes (either Widgets or Views) in a designated general package. Let's call it my.general.uibinders package.
    2. In your the UiBinder that corresponds to a concrete page (or Place), import this package as an xmlns attribute for the ui:UiBinder tag at the head of the UiBinder. This way:

      <ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" 
          xmlns:g="urn:import:com.google.gwt.user.client.ui"
          xmlns:c="urn:import:my.general.uibinders"> <!-- this is the addition -->
      
    3. From now on, in this UiBinder, c is reference to classes that reside in my.general.uibinders. Say you have a class named MyGeneralView, use it as so: <c:MyGeneralView .../> thorughout your UiBinder.