// you’re reading...

Development

Enum based ComboBoxes in GXT

Working with GXT is interesting. You combine utter frustration and time wasting with incredible breakthroughs that make you say ‘I could have never done that so easily in another framework.’ In a way all frameworks are like this I suppose.

My most recent problem/breakthrough was around enums. The particular app in question uses GXT in the browser, a set of native client apps on the users PC and an XML-RPC layer that glues them all together (yes, I still love XML-RPC, give me type safety over syntactical sugar any day of the week. Plus, we have libraries that make syntax a non-issue. But I diverge..). The client is written in C++/C# and does a lot of state tracking using, of course, enumerations, with a mirrored set of enums on the server-side.

These enums are natively persisted into the database using Cayenne’s Extended Enumeration feature making the entire transaction fairly seamless, until I got to the browser level and realized that GXT likes to treat everything as a Store and, beyond that, sending a native Cayenne class to the browser as a module was not going to work well since the Javascript Compiler would probably flip over and croak. After searching for awhile I finally came across a mix of recipes that works for me and thought I would commit my knowledge to Google and hope that I help others in the future.

Step 1: Normalize your Enum into your GWT *.client.* package space.

Yes, you must duplicate your enum OR move it to this space and change all accessors. Its a bit more complicated if your using a non-native enum (like the above mentioned Extended Enumeration for Cayenne). I ended up just making a copy of my Extended Enumerations as POE (Plain Ole Enumerations). Duplicative code AND prone to error when we change states but it worked.

Mine looks like this:

public enum BudgetPeriod {
     DAILY("Daily"),WEEKLY("Weekly"),MONTHLY("Monthly"),QUARTERLY("Quarterly"),YEARLY("Yearly");
     private String value;
     BudgetPeriod(String value) {
		this.value = value;
     }
     public String toString() {
		return value;
     }
}

Step 2: Use the SimpleComboBox

The combobox insists on a Store. At this point you can either convert your enum to ModelData and call it a day (which is what most people do) or realize that, since your using Dozer (well, I am), that this blows up fantastically and requires all sorts of conversion routines to move back and forth between ModelData and enum if Dozer is going to do its job right.

This is where the SimpleComboBox comes in, it allows you to bind any class to the combobox and thus gives you seamless abilities that glue your Enum directly to the form (and back again). It looks like this in my case:

SimpleComboBox<MyEnumClass> theComboBox = new SimpleComboBox<MyEnumClass>();

Now my box can move my enums around nicely in this combox format.

Step 3: Populate the list

Even a SimpleComboBox needs standards and its standard is either a Store or a List. We choose list and convert our Enum to that list for adding it to the list.

java.util.List enumList = Arrays.asList(MyEnumClass.values());
theComboBox.add(enumList);
This just takes all enum values and sticks them in the list for picking. Easy enough.

Step 4: Pre-select whats in the database

In an edit context we obviously must pre-select the currently selected item. Simple!
theComboBox.setSimpleValue(entity.getEnum());

Step 5: Oh no! My Dropdown list clears itself if I pick something!

This is trivial but important to understand. Since its a ComboBox at heart it is always filtering the list based on what you type. This is easily overridden by setting a trigger on the field that makes this behavior appear to go away and thus always show the list. This is a problem in two scenarios:

A) The user picked a value, thus narrowing the filter all the way to one item and clearing the picklist

B) You are in an update scenario so the pre-selection does (A) for the user (thanks!).

The answer is simple

theComboBox.setTriggerAction(TriggerAction.ALL);

This causes a lot of people confusion but it is what it is. By setting the trigger you’ll force the list to always display all of your enums (or anything else you may put in a ComboBox)

Step 6: FormBinding: Not your friend with an Enum

The next big trip-up comes when you try to bind your SimpleComboBox to the form so that it auto-updates the model and removes all that CRUD you hate so much. At the end of the day, even though we told the SimpleComboBox it contained MyEnumClass its value is just a string. When we try to bind a string to an enumeration in our backing model we get a big fat CLASS CAST EXCEPTION. Boo hiss.

The solution: Don’t bind the field, handle it manually in your create/update event right before you send your model out over RPC for commitment.

myModel.setBudgetperiod(theComboBox.getSimpleValue());

Since the combobox is pretending to hold real-enums it will pass the value of the users selecting to the model without causing that CLASS CAST EXCEPTION. This is not truly creating Enum<->Enum mapping but it is passing a value back that the model Enum *thinks* it understands and thus, making a mockery of data-types, it successfully updates the model which in turn can update the data-store transparently.

Summary and Final Code

There are other recipes for this as well as all sorts of trickery you can do for display purposes etc but in the end this worked well for my requirements. This app is really heavy on the enums and this, with a touch of code duplication and a bit more maintenance overhead for the few updates a year I may do, gave me seamless movement of enums from Windows Desktop->XML-RPC->Database->Browser/GXT->Database->XML-RPC->Windows Desktop.

Take that JSON! (just kidding, I love you also JSON. Sometimes..)

Enum

public enum BudgetPeriod {
     DAILY("Daily"),WEEKLY("Weekly"),MONTHLY("Monthly"),QUARTERLY("Quarterly"),YEARLY("Yearly");
     private String value;
     BudgetPeriod(String value) {
		this.value = value;
     }
     public String toString() {
		return value;
     }
}

SimpleComboBox creation

A few notes here:

  • I assign my list of enum values to a variable and then add that variable. You can do that in one fell swoop and avoid the assignment, this code is debugger friendly
  • Notice that I am NOT defining theComboBox here, just assigning it. You need to define it at the class level so that the update routine, which is usually anonymous in a GWT/GXT pattern, can access it without it being final.
theComboBox = new SimpleComboBox<BudgetPeriod>();
java.util.List periodList = Arrays.asList(MyEnumClass.values()); 
theComboBox.add(periodList);
theComboBox.setLazyRender(false);
theComboBox.setForceSelection(true);
theComboBox.setTriggerAction(TriggerAction.ALL);
theComboBox.setSimpleValue(project.getBudgetperiod());

Create/Update handler

Your handle will be different, this is just an example of mine. The only line you care about is ‘myModel.setBudgetPeriod(…)’
createButton.addListener(Events.Select, new Listener() {
     public void handleEvent(ButtonEvent e) {
         grid.mask("Saving..");
         myModel.setBudgetperiod(projectBudgetTimeSpan.getSimpleValue()); // THIS IS ALL YOU CARE ABOUT IN THIS CODE CHUNK
         MyRPC.Util.getInstance().createProject(project, ClientSession.getInstance().getSessionID(),
                                                new ProjectCreatedCallback(grid, index, myModelBean));
         }
});

Discussion

3 comments for “Enum based ComboBoxes in GXT”

  1. Wow, it came handy as I completely forgot how to enable selection-only.

    Also, the tips for using SimpleComboBox were handy. Thank you!

    KEEP POSTING STUFF!

    Posted by Aldrin Leal | March 21, 2010, 8:15 pm

  2. PillSpot.org. Canadian Health&Care.Best quality drugs.No prescription online pharmacy.Special Internet Prices. Online Pharmacy. Buy drugs online

    Buy:Mega Hoodia.Actos.100% Pure Okinawan Coral Calcium.Zovirax.Prevacid.Nexium.Retin-A.Petcam (Metacam) Oral Suspension.Arimidex.Lumigan.Human Growth Hormone.Zyban.Valtrex.Accutane.Synthroid.Prednisolone….

    Posted by ALVIN | July 3, 2010, 6:09 pm
  3. Posted by sandisk | August 29, 2010, 7:22 pm

Post a comment