table using arbitrary UI component: how to select?

Hi Gurus,

I’m making great progress with a vaadin app, but have got stuck with some Table elements.
My Table (actually a TreeTable) has a column containing a layout element (subclass of HorizontalLayout). It displays fine in the table, but I can’t click on that column to select the table value. I guess I must have to catch some event and forward it to the table, but I can’t find what it is. Any guidance appreciated. Code is roughly as below - it may not be complete as I’ve just cut-and-pasted the relevant, but it shows the concept. My tree works if I click on a row in the ‘name’ column, but not if I click on the ‘test’ column.

Thanks in advance,
Andy

public class EntityList extends HierarchicalContainer {
	
	final String name;
	final String test;
    
	public EntityList() {

		columnName = "stuff";
		test = "test";
		
		addContainerProperty(name, String.class, "");
		addContainerProperty(test, Component.class, "");
	}
	
	public void Refresh() {
		this.removeAllItems();
		for (....) {
				i.getItemProperty(name).setValue( "something" );
				i.getItemProperty(test).setValue( new TestEntry() );
			}
		});
	}

	class TestEntry extends HorizontalLayout {
		TestEntry() {
			setWidth("50px");
			setHeight("50px");
		}
	}
}



class MyList extends VerticalLayout
{
	private final EntityList 			entityList;
	private final TreeTable 			tree;

	public MyList (final InteractiveDeviceList idl) {
		entityList = new EntityList();
		tree = new TreeTable();
		tree.setContainerDataSource(entityList);
		tree.setSelectable(true);
		tree.setImmediate(true);
		tree.setVisibleColumns(new Object[] { entityList.name, entityList.test } );
		addComponent(tree);
	}
}

I have a similar problem to let you know you’re not alone. (Vaadin 6.7.3)

I have several Single Column Tables with a HorizontalLayout as the row. In the HorizontalLayout I have an Embedded image and a Label. The row can be selected in the table by clicking on the Embedded image OR to the right side of the Label BUT can not be selected by clicking on the label itself. The Label is Content xHtml.

When my Row was a Label Only the whole thing was selectable no problem. Must have something to do with the Layout component.

So if you put something inside of your “TestEntry” HorizontalLayout (besides a Label) it should be selectable.

I’m looking for a solution too

Thanks
Dana

I used a workaround in order to implement this thing:

The custom component that is rendered in a table cell use the LayoutClickListener in order to catch the mouse clicks and when a mouse click event is catched then the parent table must be notified about the new selection and the table selectedValue must be changed to the correct selection.

in your custom component that extends Horizontal Layout or Vertical Layout you must have this:


Callback clickHandler;

    this.addListener(new LayoutEvents.LayoutClickListener() {          
        @Override
        public void layoutClick(LayoutEvents.LayoutClickEvent event) {
            clickHandler.execute(event);
        }
    });

and now when the parent table is populated:

IndexedContainer inxdexedDS = new IndexedContainer();
table.setContainerDataSource(inxdexedDS);
table.setSelectable(true);

for (final Object schedule : schedules) {
Item item = inxdexedDS.addItem(schedule);

            Callback selectRowCallback = new Callback() {
                @Override
                public Object execute(Object arg) {
                    table.setValue(schedule);
                    return null;
                }
            };

            ScheduleOptions rowOptions = new ScheduleOptions(selectRowCallback);
            String scheduleDescription = ((ScheduleDTO) schedule).getScheduleDescription();

            item.getItemProperty("scheduleDescription").setValue(scheduleDescription);
            item.getItemProperty("options").setValue(rowOptions);

        }

public interface Callback {
Object execute(Object arg);
}

When Vaadin selects an item of a table by the mouse then the client side is changed by the css and then the server side is invoked in order to reflect the selection into the table model; because of that, after you call the table.setValue() the last clicked row of the table will still have the v-table-focus class and the selection will move to the new row and this will make have a dotted border on the old row but we don’t need it so I used a css line that removes this border: (as i know there is no solution to force the client side to refresh except the Refresher add-on or the progress component.)

.v-table .v-table-focus .v-table-cell-content{
border: none;
}

if you need many details about this solution please let me know and I will add a sample application.

Sorry if this solution does not respect the best practices of vaadin but this is my solution based on the vaadin knowledge I have now.

Think right now yours is the only solution if you want a Layout as a Table Row item. The layout captures the click first and does not pass it on to the Table.

I re-checked mine and both the Embedded Object and the Label in my HorizontalLayout will not select the row. Since I had the layout size undefined, that’s why it worked when clicking to the right of the label

For me I’m just going to change to 2 Columns and if all possible to avoid layouts as Table Rows until this is fixed.

Thanks!

Kudos hereby attributed. Many thanks!

Another approach: use Blackboard addon (very good job!!):

  1. Create a class to transfer objects when a click on table occurs:
package br.com.example;

import com.github.wolfie.blackboard.Event;
import com.github.wolfie.blackboard.Listener;

public class TableRowSelectedEvent implements Event {
	public interface TableRowSelectedListener extends Listener {
		public void receiveObject(final TableRowSelectedEvent event);
	}

	private final Object object;

	public TableRowSelectedEvent(final Object object) {
		this.object = object;
	}

	public Object getObject() {
		return object;
	}

	@Override
	public String toString() {
		return getClass().getSimpleName() + ": " + object.toString();
	}
}
  1. Add this pair (listener,event) to the application (run at startup):
BLACKBOARD.register(TableRowSelectedListener.class, TableRowSelectedEvent.class);
  1. Save the id for table row somewhere in you custom field (mine is in Data of the layout - line 9). Add a LayoutClickEvent that fires the event you created (lines 15-24).
public class ImageCustomField extends CustomField {

	private static final long serialVersionUID = 1L;

	ImageCustomField(String caption, Resource resource, Object itemId) {
		GridLayout layout = new GridLayout(2, 1);
		
		// save id for latter use
		layout.setData(itemId);
		Embedded embedded = new Embedded(null, resource);
		layout.addComponent(embedded, 0, 0);
		Label cap = new Label(caption);
		layout.addComponent(cap, 1, 0);
		layout.setComponentAlignment(cap, Alignment.MIDDLE_LEFT);
		layout.addListener(new LayoutEvents.LayoutClickListener() {
			private static final long serialVersionUID = 1L;

			@Override
			public void layoutClick(LayoutEvents.LayoutClickEvent event) {
				new BlackboardManager().getBlackboard().fire(
						new TableRowSelectedEvent(((GridLayout) event
								.getSource()).getData()));
			}
		});
		setCompositionRoot(layout);
	}

	@Override
	public Class<?> getType() {
		return Embedded.class;
	}

	public float getWidth() {
		return 400;
	}

}
  1. Make the class that has the table implements your listener, and overwrite the receiveObject (or similar) method:
(...)
public class StandardViewTable<T> extends VerticalLayout implements
		ComponentContainer, TableRowSelectedListener(
(...)
@Override
public void receiveObject(TableRowSelectedEvent event) {
       if (table.getValue() == null) {
		// select
		table.select((String) event.getObject());
	}else{
        // unselect
		table.select(null);
	}
}