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);

6 comments:

  1. This saved me hours. Thought I might share

    Here is code for editing columns based on foreign key dependencies using drop downs.

    ListGrid myGrid = new ListGrid();
    ListGridField field = new ListGridField("Foreign Key Field");
    DataSource dataSource = myDS.getInstance();
    String pKey = "myDS_id";
    String pValue = "myDS_value";

    final SelectItem dropdown = new SelectItem();
    dropdown.setOptionDataSource(dataSource);
    dropdown.setValueField(pKey);
    dropdown.setDisplayField(pValue);
    dropdown.setAutoFetchData(true);

    field.setEditorType(dropdown);
    field.setOptionDataSource(dataSource);
    field.setValueField(pKey);
    field.setDisplayField(pValue);
    field.setAutoFetchDisplayMap(true);

    myGrid.setFields(field);
    myGrid.setAutoFetchDisplayMap(true);
    myGrid.setAutoFetchData(true);

    ReplyDelete
  2. This post was helpful to me. I have another issue regarding SelectItem/ComboboxItem. When i run my application in IE, the appearance of these controls disturbs. They look so weired. What shud I do now? I cant attach image here otherwise i would show the issue.

    ReplyDelete
  3. Hi, thank you for this entry. Helped me well to get my id´s but i am having trouble by getting the displays correct. Are you using Smartgwt EE or lgpl?

    In fact i do not know how to get the setForeignKey inside my parent datasource right. Javadoc says ill need "datasource.field" but this is how i would do it in xml isnt it? I tried nearly everything but it only shows the id :(

    ReplyDelete
  4. Hi, is there a easy way to save values when I finished edit mode. Like in forms with datasource?

    ReplyDelete
  5. I have tried to implement something similar and it works for datagrid and record edit in DymanicForm but does not work for adding of a new record. Data in combobox is available but after trying to insert record there is a warning - No pointer for xpath. ValueXPath is given in xml datasource and it works for grid and save but not for add...
    What could be the reason of such behavior?

    ReplyDelete
  6. If you use the server-side SmartClient stuff that comes with the Enterprise Edition license then this can be done in the .ds.xml file. For example, if User has a role field mapped to Role's id field, and Role's name field should be displayed then...

    <field name="role" optionDataSource="Role" valueField="id" displayField="name"/>

    ReplyDelete