Spring Richclient ~ Session Eight ~ Such a bind

By the way...This content is now pretty old: check the homepage for the latest.

Let's look at the binding framework today. I've got an options dialog to write, and it looks like the binding framework could be just the ticket...

For the uninitiated, when we talk about "binding" we mean linking values in a text field on a form to values in our Java class. Essentially we want to turn:

  public class TrafficWarden {
    String name = "Trevor the Traffic Warden";
    String parkingTicketCount = 124023;
  }

into this:

And when the user hits "Save", we want the values in our java object to change.

This is a standard well-defined problem for which there exists a number of solutions. So what does Spring Rich Client give us to aid us in our quest? Let's have a look.

How do we make a form?

The first example that comes to mind of a view with fields on it is the "owner properties view" in the petclinic sample. This is shown when we right click an owner:

Let's have a look at the code which created this page, from OwnerManagerView.java:

  public void execute() {
    final Owner owner = getSelectedOwner();
    ownerFormModel = FormModelHelper.createCompoundFormModel(owner);
    ownerGeneralForm = new OwnerGeneralForm(FormModelHelper.createChildPageFormModel(ownerFormModel, null));</p>
<p>    compositePage = new TabbedDialogPage("ownerProperties");
    compositePage.addForm(ownerGeneralForm);
    compositePage.addForm(new OwnerAddressForm(FormModelHelper.createChildPageFormModel(ownerFormModel, null)));</p>
<p>    TitledPageApplicationDialog dialog = new TitledPageApplicationDialog(compositePage, getWindowControl()) {
      protected void onAboutToShow() {
        ownerGeneralForm.requestFocusInWindow();
        setEnabled(compositePage.isPageComplete());
      }</p>
<p>      protected boolean onFinish() {
        ownerFormModel.commit();
        clinic.storeOwner(owner);
        ownersTreeModel.nodeChanged(getSelectedOwnerNode());
        return true;
      }
    };
    dialog.showDialog();
  }

Ok, to create a form, we do the following things:

  1. Grab the currently selected Owner.
  2. Create a form model for this Owner. So what's one of those? Turns out that the class has got some documentation - from FormModel.java:
          A form model represents the state and behavior of a form independently from
          the UI used to present the form.
    Ok, so it's an abstract representation of a form. Fine.
  3. Create an OwnerGeneralForm, passing a "child page" version of the form model. This makes sense - we've got two tabs, one for "General Info" one for "Address". Rich Client seems to links the "child page" FormModel it creates with the main wizard's FormModel.
  4. Do a similar thing for the OwnerAddressForm.
  5. Add both of these forms to a TabbedDialogPage, which I guess does the fancy tab stuff.
  6. Create a new TitledPageApplicationDialog which takes the TabbedDialogPage we created earlier. We override this dialog to provide stuff to do when it closes.
  7. Show the dialog!

When we're done with a form, we do the following:

  1. "Commit" the form; I guess this allows it to reconstruct our data from the form fields.
  2. Store the updated owner in the clinic.
  3. Signal to the main view that we might have changed something.

Ok, so we've seen how to create a new form, and how to extract the data. Let's try one of our own, and see how far we get.

Having a go at an options dialog

Ok, first things first - create an options command in commands-context.xml and add it to the edit menu. I was going for the tools menu, but I don't need one for this app, so I'll just call the menu option 'Preferences' and be all 'mac-like' :)

<bean id="optionsCommand" class="uk.co.myco.myproj.command.OptionsCommand">
<property name="optionsDao"><ref bean="myDao"/></property>
</bean>
...
<bean id="editMenu" class="org.springframework.richclient.command.CommandGroupFactoryBean">
<property name="members">
<list>
      <value>selectAllCommand</value>
      <value>separator</value>
      <ref bean="optionsCommand"/>
    </list>
  </property>
</bean>

I need a data access object (DAO) wired into the "OptionsCommand", as the command has got to load and save the options from somewhere...

Ok, so we create a new command class in OptionsCommand.java and come to writing doExecuteCommand() (see session seven for details on writing commands). First we need the existing options, and then we need to create a FormModel based on these options, as explained above:

final Options opt = optionsDao.getOptions();
optionsForm = new OptionsForm(FormModelHelper.createFormModel(opt));

As we're not splitting up the options onto different pages (there are only three for this application!) we don't need any of that compound form model stuff. A simple call to FormModelHelper.createFormModel() is all we need.

We'll come to writing OptionsForm in a minute.

After that, we need a new dialog:

TitledPageApplicationDialog dialog = new TitledPageApplicationDialog(new FormBackedDialogPage(optionsForm), null) {
  protected boolean onFinish() {
    optionsForm.commit();
    optionsDao.storeOptions(opt);
    return true;
  }
};

This seems straightforward. We create a new TitledPageApplicationDialog, with a FormBackedDialogPage (and therefore our optionsForm) controlling the contents. We override the onFinish() method to 'commit' the form and save the options in the DAO.

Then we show it:

dialog.showDialog();

...and that's it for OptionsCommand! Easy, huh?

Hang on, you're probably thinking: what about supplying the text to go into this dialog? This is all controlled by the FormModel we supplied, our own OptionsForm. Let's take a look at how that is implemented:

public class OptionsForm extends AbstractForm
{
  public OptionsForm(FormModel model)
  {
    super(model, "optionsForm");
  }</p>
<p>  protected JComponent createFormControl()
  {
    TableFormBuilder builder = new TableFormBuilder(getBindingFactory());
    builder.add("url");
    builder.row();
    builder.add("user");
    builder.row();
    builder.add("password");
    return builder.getForm();
  }
}

I've shown the whole class, simply to show how short it is. The createFormControl starts up a new builder, adds a "url" field, a "user" field and a "password" field and then spits out a JComponent (the basic Swing GUI object, for the uninitiated). There's not much to creating these FormModels, is there?

Note that a lot of what I've done here is based on convention. The names of the rows given to the form builder are expected to exist as JavaBean properties in the object you pass to the model. For example, when I do this in OptionsCommand:

final Options opt = optionsDao.getOptions();
optionsForm = new OptionsForm(FormModelHelper.createFormModel(opt));

...the Options object must contain getUrl()/setUrl(), getUser()/setUser(), and getPassword()/setPassword() methods for this to work. I'm a big fan of convention over configuration, it saves a lot of hassle.

How to we get those aforementioned messages to work? In the constructor show above, you pass a string detailing what you want the form to be called (mine is called "optionsForm", although it could easily be called "allyourbasebelongtous" if desired). Then, you simply add message keys to messages.properties based on that key:

optionsForm.title=Options
optionsForm.description=Change options <b>Note:</b> Clicking on 'OK' will save the options.<br />

Note that you can use basic HTML for formatting. Lovely.

The actual field names are controlled by further message keys in messages.properties, the names of which are simply the names of the JavaBean properties of our Options class:

url.label=The U&amp;rl
user.label=&amp;Username

(Password already seems to have an internal Spring Rich Client label set up, so I didn't bother adding it)

And... we're done. We're done? Surely not!

No, we really are done. That wasn't at all hard, was it? When I first implemented this, after I got it compiling, it actually worked first time, too, which shows how little code I needed to write to make it work; if there was any more than a tiny amount of code to write, I would have made a mistake :)

Regular Swing types are probably thinking: "That's it? There must be some catch!" No, that really is all the coding you have to do to get a bound form up and running in Spring Rich Client; and there's not a GridBagLayout in sight. This framework is rather good, isn't it? :)

Next time, we'll probably look the validation framework to ensure the user actually types in valid values...


Share on BlueSky to comment.