Editable Table TextChangeListener problem: adding new row clears edited row

Hi there, I’m new to using Vaadin. It all seems very nice, but I’m having some problems with editable tables.

I want to use an editable table as an input mask for data rows, similar to a spreadsheet. There is no limit on the number of rows. I’d like to always have an empty row at the bottom of the table which can be used for entering a new row, which when filled in, will make the table add another new empty row at the bottom.

My approach was to use Table’s isLastId() method to check if the changed item is the last row, and if so, add another empty row.

Getting the item id of the changed row required some rather ugly reflection hacking. I read about selectable Tables and their getValue() method which can be used to query the selected item id, but that doesn’t seem to work for editable tables; it always returns null. Is there a nicer way of doing this? It would be nice if editable Tables also implemented getValue().

Okay, the problem I’m now stuck on is when I call addEmptyRow() after I check whether or not the edited rows was the last one. A new empty row gets successfully added to my table, but the text field I was filling in just before the TextChangeEvent fired, gets reset, i.e. it’s empty again and effectively I end up with 2 empty rows. (Filling in the row I was filling in, which is now the second to last row, won’t add another when changed, as it isn’t the last one anymore).

Anyone got an idea why adding another row from within my TextChangeListener causes this to happen?

Here’s my Table code:


   public void addEmptyRow()
   {
      final int nColumns = getContainerPropertyIds().size();
      final Object[] columns = new Object[nColumns]
;
      for (int i = 0; i < nColumns; i++)
      {
         columns[i]
 = "";
      }
      addItem(columns, null);
   }

   private final class InnerTextChangeListener implements TextChangeListener
   {
      @Override
      public void textChange(TextChangeEvent event)
      {
         // Add another row to the bottom when the bottom one gets filled in
         Integer selectedRowId = null;
         try
         {
            // Reflection hacking to get the item id :/
            final TextField source = (TextField) event.getSource();
            final Property propertyDataSource = source.getPropertyDataSource();
            final Field itemIdField = propertyDataSource.getClass().getDeclaredField("itemId");
            itemIdField.setAccessible(true);
            selectedRowId = (Integer) itemIdField.get(propertyDataSource);
         }
         catch (Exception e)
         {
            throw new RuntimeException(e);
         }

         if (isLastId(selectedRowId))
         {
            addEmptyRow();
         }
      }
   }

And my FieldFactory code:


public class TextChangeListenerFieldFactory extends DefaultFieldFactory
{
   private final TextChangeListener listener;

   public TextChangeListenerFieldFactory(TextChangeListener listener)
   {
      this.listener = listener;
   }

   @Override
   public Field createField(Container container, Object itemId, Object propertyId, Component uiContext)
   {
      final TextField field = (TextField) super.createField(container, itemId, propertyId, uiContext);
      field.setImmediate(true);
      field.addListener(this.listener);
      return field;
   }
}

PS: is there any way I can format Java code more nicely here?

Is there a better way to do it after 2 years? I’m looking for the exact UI experience but don’t know how to do it?

Hello,
I needed to develop the same thing and, inspired by your code, have found a relatively simple implementation.

Table: [code]

Table table = new Table(“Values:”);
table.setContainerDataSource(new BeanItemContainer(EnumValueDescriptor.class));
table.setEditable(true);
table.setImmediate(true);
table.addItem(new EnumValueDescriptor());
table.setTableFieldFactory(new BeanFieldFactory(EnumValueDescriptor.class));

[/code]

Field factory:[code]

public class BeanFieldFactory extends DefaultFieldFactory {

private final Class<T> beanType;

public BeanFieldFactory(Class<T> type){
	this.beanType = type;
}

@Override
public Field<?> createField(final Container container, final Object itemId,
		Object propertyId, final Component uiContext) {
	final Field<?> field = super.createField(container, itemId, propertyId, uiContext);
	if (field instanceof AbstractTextField) {
		((AbstractTextField) field).setNullRepresentation("");
	}
	((AbstractComponent)field).setImmediate(true);
	field.addValueChangeListener(new ValueChangeListener() {
		
		@Override
		public void valueChange(ValueChangeEvent event) {
			Object lastItem = null;
			for (Object item : container.getItemIds()) {
				lastItem = item;
			}
			if (itemId == lastItem && event.getProperty().getValue() != null) {
				try {
					((Table)uiContext).addItem(beanType.newInstance());
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	});
	return field;
}

}

[/code]

But I haven’t found a way to preserve focus to behave properly: when a new item is added, the newly selected field looses the focus gained after Tab key event.