Table's generated column and component reuse

Hi

By asking this question, I am wondering whether I may simply have lost my way somewhere, but here I am.
I use a Table with several generated columns showing composite components, e.g. :

ui_table.addGeneratedColumn("progression", new Table.ColumnGenerator() {
    public Component generateCell(Table source, Object itemId, Object columnId) {
        final Task task = (Task)source.getItem(itemId).getItemProperty("task").getValue();
        return new ProgressionWidget((float)task.getProgression());
    }
});

… where ProgressionWidget consists of a layout with a progress bar and a couple of buttons and labels.
And as its name implies, its instances’ values can change often while correponding tasks’ progressions goes up to 100%.
I think it would be way more clever to update existing ProgressionWidget instances instead of constructing new instances all the time and having them replace the old ones, but I just can’t figure out how to do that.
The following code does not work (see comment inside) :

ui_table.addGeneratedColumn("progression", new Table.ColumnGenerator() {
    public Component generateCell(Table source, Object itemId, Object columnId) {
        final Task task = (Task)source.getItem(itemId).getItemProperty("task").getValue();
        ProgressionWidget widget = (ProgressionWidget)source.getItem(itemId).getItemProperty(columnId).getValue();
        if (widget == null) // <---- IS ALWAYS NULL !
            widget = new ProgressionWidget((float)task.getProgression());
        else
            widget.updateProgression((float)task.getProgression());
        return widget;
    }
});

I am using an IndexedContainer as data source for my table.
Somebody help me :slight_smile:

Hi,

rogressionWidget widget = (ProgressionWidget)source.getItem(itemId).getItemProperty(columnId).getValue();

This will alays return null, because generated column does not relates to a datasource, but to a table view. It is expected that column generator will create a component when invoked.

I think, you may try to cache your progress widgets in a hash map ( I’d recommend to use weak references ) by the ID key and lookup them form there in your column generator.

 ui_table.addGeneratedColumn("progression", new Table.ColumnGenerator() {
     public Component generateCell(Table source, Object itemId, Object columnId) {
         final Task task = (Task)source.getItem(itemId).getItemProperty("task").getValue();
         ProgressionWidget widget = widgetsCache.get(itemId)
         if (widget == null) 
             widget = new ProgressionWidget((float)task.getProgression());
               widgetsCache.put(itemId, widget);
         else
             widget.updateProgression((float)task.getProgression());
         return widget;
    }
});

Thanks for your answer.
I was hesitating to do so, but if you suggest that, I guess it is a viable solution!

However, before it give it a try, I have another (closely related) question :

Since generated columns are not sortable and that’s a shame for my “progression” column, isn’t a simple way to let “progression” be a regular column of type Float but override default display to have ProgressionWidgets display the progession information, while not resorting to a generated column?

Thanks

I think you can get the generated column sortable by having the additional bean property in your bean item or whatever represents a row in your table model and then registering a generated column over this field.

Check the following topic for details:

http://vaadin.com/forum/-/message_boards/message/64250

Thanks for your answer. I read the other discussion but I am afraid the solution provided there does not match my case.

I am using an IndexedContainer as datasource for my table. I add/modify/remove items programmatically to the container from time to time.

One of the container’s native property is “progression” and of type Float.class, but I don’t want the default display in this column (labels displaying the Floats toString()), I want a custom widget of mine instead.

Since I did not find a simple way to do so (maybe I missed it?), I decided to hide the native “progression” column and to declare an additional, generated “progression2” column to the table which constructs a new instance of my ProgressionWidget class each time generateCell is called (yik!), simply passing the progression value to its ctor. Plus, I lose ability to sort (double yik!!).

Since this is all highly unacceptable, I am telling to myself that I missed something obvious… but what?

I had the same feeling until I stumbled upon this
http://vaadin.com/api/com/vaadin/data/util/PropertyFormatter.html

I am not aware of an example other than the snippet in the javadoc.

Thanks for your answer, that’s interesting indeed.
However, this does not seem to address what I am looking for, since it only allows to return a custom string.

Say, I have a simple read-only table with a column of type Boolean.class, which is sortable, but instead of displaying “true” and “false” (which is the default) I want to display a green check or a red cross icon.
Yes, I need some kind of proxy class that I install on the column, or whatever, but I just cannot find it…

The normal way to do this is to return a (new) Label (or Embedded) component from a
TableFieldFactory
. If you are using a generated column, return such a component directly from generateCell() instead.

You might also be interested in simply styling table cells with CSS. This can be achieved using a
Table.CellStyleGenerator
(see
example
).

Thanks for your tips.

I have not tried out TableFieldFactory yet since I read everywhere that this is reserved for cell editing. Did I get it wrong? I have a read-only table. Is there a way to install a TableFieldFactory for pure data display?

I had a look at the CellStyleGenerator example. I could rephrase my problem on this example’s basis like this:
My datasource already has an “email” property, which I want to display like in the example. No need for a generated column since I already have the “email” property, plus I want to be able to sort by email address. How do I stick emails in Link objects and keep ability to sort? I am not a CSS guy but I am pretty sure styling won’t help here :mellow:

For having something active like links in a non-editable table while preserving sortability, the options are somewhat more limited. A generated column would otherwise fit but is not sortable.

You probably need to either have the components directly in the container and make the container able to sort them, or override Table.getPropertyValue(). The latter is usually easier, YMMV.

Thanks again for your answer.
I am now playing around with Table.getPropertyValue() .

Here is my code that is supposed to display a green or red icon for a property named “myBoolProperty” and of type Boolean.class:


class CustomTable extends Table {
    protected Object getPropertyValue(Object rowId, Object colId, Property property) {
        if (colId.equals("myBoolProperty")) {
            boolean value = property.getValue() == null ? false : (Boolean)property.getValue();
            String url = value ? "http://xxx/greenicon.gif" : "http://xxx/redicon.gif";
            return new Embedded(null, new ExternalResource(url));
        }
        return super.getPropertyValue(rowId, colId, property);
    }
}

And here is why I get at runtime:


java.lang.ClassCastException: com.vaadin.ui.Embedded cannot be cast to java.lang.String
	at com.vaadin.ui.Table.paintContent(Table.java:2254)
	at com.vaadin.ui.AbstractComponent.paint(AbstractComponent.java:660)
	at com.vaadin.ui.AbstractOrderedLayout.paintContent(AbstractOrderedLayout.java:150)
	at com.vaadin.ui.AbstractComponent.paint(AbstractComponent.java:660)
	at com.vaadin.ui.TabSheet.paintContent(TabSheet.java:281)
	at com.vaadin.ui.AbstractComponent.paint(AbstractComponent.java:660)
	at com.vaadin.terminal.gwt.server.CommunicationManager.paintAfterVariablechanges(CommunicationManager.java:492)
	at com.vaadin.terminal.gwt.server.CommunicationManager.handleUidlRequest(CommunicationManager.java:331)
...

I am feeling a bit upset :frowning:
What I want to achieve seems quite common to me. I must be doing something wrong !

I apologize for the many round-trips. I have not had the time to write test or sample code for this myself.

In fact, only generated columns and columns of a Component type are rendered as components rather than as strings.

You could also override Table.getType() as well, returning Component.class (or a subclass of it) for the columns (properties) of interest, but I have not tested this.

Thanks again for your answer.

I am not too sure about your getType() idea… unfortunately I can’t afford more time right now on that issue :frowning:

It would definitely be appreciable to have a simple way to set a reusable, sorting-compatible proxy renderer for columns even in read-only mode.

How about doing it the simpler way like this:
Adding POJOs to Table using formatting and sorting still works

I also did add PropertyFormatter into BeanItemContainer succesfully. In my API you can set PropertyFormatter per every propertyId and if such setting exists, then normal Property is wrapped into PropertyFormatter. This seems to work fine but required changes to BeanItemContainer (and BeanItem). Property values are displayed inside table correctly with nice formatting and sorting still works out of the box.

I guess above should be added as an enhancement ticket for Vaadin.

Created these tickets:


4445


4446