What’s the best way for converting data between a Table/Form on one hand and the underlying business object on the other? For instance, in my business object, I store currency as an integer (in “cents”, essentially). However, in Table or Form views I want to display it as a proper decimal number. Likewise, after editing the form, I want to convert it back from the decimal-formatted string to an integer. The conversions are trivial. But what’s the best way to plug those into Table and Form?
I’ve considered the following options:
Doing the format conversion in my business object by adding formating/parsing getters and setters. Then I can bind the table column and form field directly to those properties. However, as I see it, those conversions really don’t belong in the business objects, since they’re more of a UI issue. Furthermore, for objects with multiple currency fields, I would have to implement additional getters/setters for each such propery, all essentially doing the same thing.
Using a custom FormFieldFactory to produce custom fields (e.g., made using FieldWrapper). However, since the field I want really is just a regular text field, it doesn’t feel appropriate having to create a specialized text field just to do the format conversions. Besides, this solution won’t help for the Table, so I have to add custom code there also (e.g. using computed columns).
Using a hybrid of the two, where FormFieldFactory is used for Forms, and an additional getter is used on the business object to get the formated data. However, this results in two separate properties linked to the same underlying data, causing trouble when I change the data through the form, as the Table data is then not updated (presumably because no change notification comes from the “conversion property” added to the business object, so the table won’t know it’s changed).
I’m looking for clean way to plug in some “converter object” between the business object and the view/editor components, that can take care of those conversions. Note that the business object is sometimes a plain POJO, but I also use the HbnContainer for persistance, which sometimes returns its own wrappers for the POJO.
This will work with AbstractFields. For my projects, I have also created a sublass of table that allows you to associate a Table column with a PropertyFormatter. Sort of like this - just typed into the editor, not cut-and-pasted from code, so bound to be errors:
class MyTable extends Table {
Map<Object, PropertyFormatter> colIdToFormatter = new HashMap<Object, PropertyFormatter>();
/* Obviously methods to add propertyFormatters to the map */
@Override
protected String formatPropertyValue(Object rowId, Object colId, Property property) {
PropertyFormatter formatter = colIdToFormatter.get(colId);
if (formatter == null) {
return property.toString();
} else {
return formatter.format(property.getValue());
}
}
}
Hope that helps,
Cheers,
Charles
Edit : and now, looking in the forum, I see that you already know about PropertyFormatter. Oh well! Could be useful information for someone else…
In Table, either override
formatPropertyValue() or use a column generator. See
CarView.java for example. It uses the column generator, but I think overriding the formatPropertyValue() is a better solution for such cases.
In Form, use the PropertyFormatter. See
FillEditor.java for example.
This way, you don’t need to have any Vaadin/UI related extra bloat in your business objects. In this example, the
Fill object is just a regular bean (doesn’t actually have to be full bean).
This occurs if you have bound the underlying bean separately to Table with one wrapper and to Form with another. If you wrap the beans in a BeanItem, put those in a BeanItemContainer for the table, and edit the same bean items, the BeanItem relays the value change notifications from the Form to the Table.
For example, in the
UserView.java you have this problem for the Car objects, which are bound to the Table with a CollectionContainer and to the editor Form with a BeanItem - two separate bean bindings. So, because the BeanItem can’t communicate its value changes to the CollectionContainer, you have to recreate the entire table to update the values.
But in the
CarView , the Fill beans are wrapped only once inside a BeanItem and bound to the Table using BeanItemContainer.
I looked at FillEditor.java, but found no mentioning of PropertyFormatter there.
In the end, I solved it using a custom FieldFactory which produced a custom currency field created using the FieldWrapper class (quite handy). In my FieldFactory I used bean annotation to mark those properties that correspond to currency, allowing this mechanism to be activated merely by annotating the properties accordingly. Hence, this would be easily extensible and reusable to other types of customized fields using the same custom FieldFactory.
For the Table case, I took your advice and subclassed Table and overriding formatPropertyValue.
It would seem to me that being able to easily “plug in” a value formatter/parser for desired properties in Form and Table would be useful. Using a custom FieldFactory plus a custom field in this case felt like overkill, since I really didn’t want any other kind of field than the stock TextField. I just want to inject a “proxy” that transforms the property value back and forth between the field and the propery. I just couldn’t find an easy and clean way to do that for Form and Table. Perhaps I’m overlooking something here, but looking at other related forum posts, I think I’m not, Perhaps something to improve in Vaadin 7?