Monday 15 March 2010

Endless loop in smartGWT listgrid retrieving data from REST back end

So you have written your DataSource, instantiated your ListGrid and tested the calls to your back end REST (e.g. Rails controller) in your browser. You know that the back end is delivering the data in correct JSON, but yet your smartGWT is looping endlessly, loading the data.

The console shows no errors but loads of RPC calls.

Check your record XPath in your DataSource.The value given for the call to setRecordXPath must match precisely the JSON data path. If you get this wrong, smartGWT fails silently, looping forever attempting to get the records and it knows from the meta data should be there!

Sunday 28 February 2010

Displaying lookups from other tables in ListGrid

Here's a problem in smartGWT: You have a ListGrid that displays data from a table table. One or more columns are integer IDs that refer to other tables. Those tables have meaningful text in them (e.g. code-description tables or perhaps account or client names).

Let's assume that you are displaying client data. Rather than displaying the internal row ID for the client, you want to display the client name using the foreign key ID on your ListGrid data to look up the name in the client table.

This is easy in smartGWT. You need to use a facility called "OptionDataSource". The smartGWT documentation says that you need to set OptionDataSource on the column in question.

Now the smartGWT documentation leads you up the gardent path a bit with using this facility. Put bluntly, their method doesn't work when you have a ListGrid that is based entirely on a datasource. This is because you cannot use ListGrid.getField(...) to get the column in order to set OptionDataSource because, until it's displayed, the ListGrid is empty. So you continually get an error trying to set attributes on a null!

To work around this problem. smartGWT has ListGrid..setUseAllDataSourceFields. If you set true for this, this means that you can now create one or more ListGridField types, call setOptionDataSource on these new objects as well as setValueField and setDisplayField and then, crucially, at the end of all this, you call ListGrid.setFields to add these to the ListGrid.

Here's the part that is very unclear in the smartGWT documentation: You need to use setName to name your new objects to the same name as the columns in the ListGrid's datasource that you want to set the OptionDataSource values for. What happens is that smartGWT will then use your new objects to overwrite the objects in the datasource, i.e. you are replacing those columns with your own versions of those columns.

The part that is really not clear in any documentation or forum entries is that this is how you are establishing the mapping between the column in the ListGrid datasource that has the ID (in our example, the client ID) that you wish to display the meaningful text for from the alternative table (OptionsDataSource). In our example this is the client's name.

Specifically:
   ListGridField.setName: Set to the ListGrid datasource column that you wish to implement the lookup on.
   ListGridField.setOptionDataSource: Set the alternative datasource that contains the lookup data (must have the code and the displayed value)
  ListGridField.setValueField: Set the column in the alternative datasource that has the code values that match those in the ListGrid column that you specified in setName above (in our example, this is the client id column of the lookup table). After you have set this as well as setName above, smartGWT now has both column names that are used to lookup the data.
  ListGridField.setDisplayField: This tells smartGWT what column to use to display instead of the ID (in our example, this is the client name column of the lookup table).

Here is some sample code that implements a lookup of Group Name based on a group_id in the ListGrid datasource. The ListGrid object is recipientGrid and the lookup datasource is GroupHeaderDS.

        ListGridField groupID=new ListGridField();
        groupID.setOptionDataSource(GroupHeaderDS.getInstance());
        groupID.setValueField("group_id");
        groupID.setDisplayField("group_name");
        groupID.setName("group_id");
        groupID.setTitle("Group Name");
        groupID.setAutoFetchDisplayMap(true);
        recipientGrid.setFields(groupID);
        recipientGrid.setAutoFetchDisplayMap(true);

Thursday 18 February 2010

smartGWT - how to implement a date and time picker/chooser

SmartGWT is severly lacking a nice date/time picker/chooser. So for now, you need to use a work-around. The solution is to use the date picker in combination with a suitable method of selecting time. The standard TimeItem in smartGWT is particularly shoddy and user-unfriendly.

What you want is a spinner for the hours, a spinner for the seconds and a decent defaulting to today's date and time.

Well, fret no more, here is a solution. For the purposes of this example, we are implementing a send timestamp where the user selects a date and time to send an item. It gets placed on a dynamic form item so that it can be added to a layout.

Note that I set the borders here so that you can see the way that it's laid out. You will want to remove those lines in the real world.

  final DynamicForm scheduleForm = new DynamicForm();
  DateItem sendDate  = new DateItem();
   sendDate.setDisplayFormat(DateDisplayFormat.TOSERIALIZEABLEDATE);
  sendDate.setEnforceDate(true);
  sendDate.setRequired(true);
  sendDate.setInputFormat("YMD");
  sendDate.setTitle("Send at:");

  // This part sets up the spinners for choosing a time to send at
  // We need to default it to now
       
  Date rightNow= new Date();
  int hour= rightNow.getHours();
  int min=rightNow.getMinutes();

  SpinnerItem sendTimeHr = new SpinnerItem();
  sendTimeHr.setName("sendTimeHr");
  sendTimeHr.setMax(23);
  sendTimeHr.setMin(0);
  sendTimeHr.setTitle("Time:");
  sendTimeHr.setWidth(2);
  sendTimeHr.setDefaultValue(hour);

  SpinnerItem sendTimeMin = new SpinnerItem();
  sendTimeMin.setName("sendTimeMins");
  sendTimeMin.setMax(59);
  sendTimeMin.setMax(0);
  sendTimeMin.setTitle(" ");
  sendTimeMin.setDefaultValue(min);
       
  scheduleForm.setNumCols(6);
  scheduleForm.setWidth(414);
  scheduleForm.setBorder("2px solid black");
  scheduleForm.setCellBorder(1);
  scheduleForm.setFields(sendDate,sendTimeHr,sendTimeMin);

Getting current time in GWT

The Calendar class isn't supported in GWT because the Java cannot be compiled into JavaScript.

So for now, you need to use the deprecated Date class in Java:

  import java.util.Date;
  .
  .
  .
  Date rightNow= new Date();
  int hour= rightNow.getHours();
  int min=rightNow.getMinutes();

I am still trying to find a better supported way to solve this problem, but for now, deprecated code seems to be the best solution available.

Wednesday 17 February 2010

How to add a Date and Time popup to smartGWT

There are a lot of queries about how to add a popup for selecting time and date in smartGWT. The bottom line is that smartGWT is lacking a single widget to do this, so you have to use a DateItem and TimeItem together.

Here is some sample code that adds these items to a VLayout call leftPanel:

 final DynamicForm scheduleForm = new DynamicForm();
 DateItem sendDate  = new DateItem();
 TimeItem sendTime = new TimeItem();
       
 scheduleForm.setFields(sendDate,sendTime);
 leftPanel.addMember(scheduleForm);

Saturday 30 January 2010

smartGWT MenuBar - this.menus is undefined error

When you are attempting to add a smartGWT MenuBar to your screen, usually at system startup. If you get an error like this:

  com.google.gwt.core.client.JavaScriptException: (TypeError): this.menus is undefined

then check that you have used setMenus in when you first instantiate the menu bar. For example:
            MenuBar menuBar = new MenuBar();
           
            menuBar.setMenus(myFirstMenu,mySecondMenu);
           
            menuBar.setVisible(true);
            menuBar.setKeepInParentRect(false);

            RootPanel.get("menuPanel").add(menuBar);       


It is possible to use the add functions to add members and menus, but the underlying JavaScript needs a setMenu call to have the array that stores the menus initialised.

smartGWT MenuBar - setMenus and addMenus undefined

If you are using smartGWT and you are attempting to use the smartGWT MenuBar class, but keep getting "setMenu undefined" or "addMenu undefined" errors, then check your imports.

Google have a MenuBar in GWT as well. The package is com.google.gwt.user.client.ui.MenuBar.

The correct package to be using is  com.smartgwt.client.widgets.menu.MenuBar.