ValueChangeListener to listen changes on table data: ValueChangeEvent don’t

Hi all,
I need to save my entity, that displayed into the table, if table data were changed.
I ‘m registered the implementation of com.vaadin.data.Property.ValueChangeListener, but the method valueChange(ValueChangeEvent) was not fired, if I change the text data into table cell.
This problem is reproducible, if i use the coding of http://demo.vaadin.com/sampler#TableCellStyling example with a little lot of modifications: register the new listener
table.addListener(new TableValueChangeListener());
and set the following table setting

table.setImmediate(true);
table.setSelectable(true);
table.setWriteThrough(true);

Is it bug or I do something wrong??
P.S Details:
I call the application, set the tableto the edit mode and then entire the text into cells.
I try to change the row/cell, the ValueChangeEvent event is not fired.
This event will be fired, if i select the table row.
The full Example Code:

package com.example;

import java.util.HashMap;
import java.util.HashSet;
import com.vaadin.data.Item;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.event.Action;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.Link;
import com.vaadin.ui.Table;
import com.vaadin.ui.Table.CellStyleGenerator;
import com.vaadin.ui.VerticalLayout;

@SuppressWarnings("serial")
public class TableEdingAndStylingExample extends VerticalLayout {

	private final class TableValueChangeListener implements ValueChangeListener {

		public void valueChange(ValueChangeEvent event) {
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>TableValueChangeListener ValueChangeEvent fired>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<");

		}
	}

	Table table = new Table();

	HashMap<Object, String> markedRows = new HashMap<Object, String>();

	HashMap<Object, HashSet<Object>> markedCells = new HashMap<Object, HashSet<Object>>();

	static final Action ACTION_RED = new Action("red");

	static final Action ACTION_BLUE = new Action("blue");

	static final Action ACTION_GREEN = new Action("green");

	static final Action ACTION_NONE = new Action("none");

	static final Action[] ACTIONS = new Action[]
 { ACTION_RED, ACTION_GREEN, ACTION_BLUE, ACTION_NONE };

	public TableEdingAndStylingExample() {
		setSpacing(true);

		addComponent(table);

		// set a style name, so we can style rows and cells
		//        table.setStyleName("contacts"); ich habe das auskommentiert zum testen.

		// size
		table.setWidth("100%");
		table.setPageLength(7);

		// connect data source
		table.setContainerDataSource(ExampleUtil.getPersonContainer());

		// Generate the email-link from firstname & lastname
		table.addGeneratedColumn("Email", new Table.ColumnGenerator() {
			public Component generateCell(Table source, Object itemId, Object columnId) {
				Item item = table.getItem(itemId);
				String fn = (String) item.getItemProperty(ExampleUtil.PERSON_PROPERTY_FIRSTNAME).getValue();
				String ln = (String) item.getItemProperty(ExampleUtil.PERSON_PROPERTY_LASTNAME).getValue();
				String email = fn.toLowerCase() + "." + ln.toLowerCase() + "@example.com";
				// the Link -component:
				Link emailLink = new Link(email, new ExternalResource("mailto:" + email));
				return emailLink;
			}

		});

		// turn on column reordering and collapsing
		table.setColumnReorderingAllowed(true);
		table.setColumnCollapsingAllowed(true);

		// Actions (a.k.a context menu)

		table.addActionHandler(new Action.Handler() {
			public Action[] getActions(Object target, Object sender) {
				return ACTIONS;
			}

			public void handleAction(Action action, Object sender, Object target) {
				markedRows.remove(target);
				if (!ACTION_NONE.equals(action)) {
					// we're using the cations caption as stylename as well:
					markedRows.put(target, action.getCaption());
				}
				// this causes the CellStyleGenerator to return new styles,
				// but table can't automatically know, we must tell it:
				table.refreshRowCache();
			}

		});

		// style generator
		table.setCellStyleGenerator(new CellStyleGenerator() {
			public String getStyle(Object itemId, Object propertyId) {
				if (propertyId == null) {
					// no propertyId, styling row
					return (markedRows.get(itemId));
				} else if (propertyId.equals("Email")) {
					// style the generated email column
					return "email";
				} else {
					HashSet<Object> cells = markedCells.get(itemId);
					if (cells != null && cells.contains(propertyId)) {
						// marked cell
						return "marked";
					} else {
						// no style
						return null;
					}
				}

			}

		});

		table.addListener(new TableValueChangeListener());
		// toggle cell 'marked' styling when double-clicked
		table.addListener(new ItemClickListener() {
			public void itemClick(ItemClickEvent event) {
				if (event.getButton() == ItemClickEvent.BUTTON_RIGHT) {
					// you can handle left/right/middle -mouseclick
				}

				if (event.isDoubleClick()) {
					Object itemId = event.getItemId();
					Object propertyId = event.getPropertyId();
					HashSet<Object> cells = markedCells.get(itemId);
					if (cells == null) {
						cells = new HashSet<Object>();
						markedCells.put(itemId, cells);
					}
					if (cells.contains(propertyId)) {
						// toggle marking off
						cells.remove(propertyId);
					} else {
						// toggle marking on
						cells.add(propertyId);
					}
					// this causes the CellStyleGenerator to return new styles,
					// but table can't automatically know, we must tell it:
					table.refreshRowCache();
				}
			}
		});


		table.setImmediate(true);
		table.setSelectable(true);
		table.setWriteThrough(true);
		// edit button
		final Button editButton = new Button("Edit");
		addComponent(editButton);
		editButton.addListener(new Button.ClickListener() {
			public void buttonClick(ClickEvent event) {
				table.setEditable(!table.isEditable());
				editButton.setCaption((table.isEditable() ? "Save" : "Edit"));
			}
		});
		setComponentAlignment(editButton, Alignment.TOP_RIGHT);
	}
}

The code you posted is not very clear, but I gather that you are displaying a collection of beans in a table, similar to a database table. Then you modify a value (by changing the content of a text field), and then you want to store the result?

If this is correct, then a ValueChangeListener on the Table won’t help you. That will only tell you that a row in a table has been selected (this is the value of a Table), not that the content of the row has changed (see e.g.
this example
). You need to listen to the changes in each cell instead.

The default behaviour for setEditable() uses the default FieldFactory to generate Fields from you String values, so that you can edit the content of the Container inside the Table. To listen to changes in the generated fields, you need to extend the standard FieldFactory and add ValueChangeListeners to each generated field. These will then tell you when someone has edited a cell in the Table (remember to make the fields immediate as well).

Hope this helps.

Thank you Thomas,
I have now used similar solution. I implement the
FieldEvents.TextChangeListener
interface.
P.S. It is a pity that the table has no event, which would be fired if the data table content has changed.

Well, it’s not really what the Table was designed to do. I do agree that it would be beneficial in certain situations, as your project demonstrates. I don’t think it will make it into the product, though :slight_smile:

Such a shame that Vaadin provides functionality only for simple applications.