0

I have a simple Table where I present some data using an IndexedContainer as data source. I want my users to be able to edit some of this data, so I'm using a TableFieldFactory (DefaultFieldFactory) to generate these columns (properties) editable.

Now, if a user is in editing mode (table.setEditable(true)) and have been changing some fields, he or she should be able to discard those changes - for this purpose, I have a "Cancel" button. This "Cancel" button is supposed to discard all changes made in the Table generated fields since the user entered editing mode and then setEditable(false) - now everything should be the way it was before setEditable(true) was called.

This didn't sound very hard, until I tried implementing it.

If I understand the functionality of the Table vs. Container vs. TableFieldFactory correctly, the following happens:

  1. Properties are added to the Container
  2. The Container is set as the Table data source
  3. The User clicks the "Edit Table" button (table.setEditable(true))
  4. The Table calls the TableFieldFactory's overridden createField() method
  5. The createField() method creates the editable fields
  6. The user edits the fields and at the same time the Container gets updated <-- not 100% sure about this one
  7. The user clicks the "Cancel" button <-- HERE is my problem

Question: When I press the "Cancel" button, what should I do discard() on? I can't do table.discard(), because the changes has already taken place. I can't do container.discard() becase, yeah, the Container interface doesn't inherit that method. I can't do field.discard(), because I cannot reach the fields from outside the createField() method.

I have tried different variations of setBuffered, markAsDirty, refreshRowCache and setImmediate without success.

Here's (hopefully all) relevant code:

The table, container and the "Cancel" button (roughly):

table.setContainerDataSource(container);

Button cancel = new Button("Cancel", new Button.ClickListener() {
    private static final long serialVersionUID = 1L;

    @Override
    public void buttonClick(ClickEvent event) {
        // table.setImmediate(false); //tried variations of this
        // table.refreshRowCache(); //tried variations of this
        // table.markAsDirty(); //tried variations of this
        // table.setBuffered(true); //tried variations of this
        // table.discard(); //tried this, but here it's too late
        table.setEditable(false);
    }
});

The TableFieldFactory:

table.setTableFieldFactory(new DefaultFieldFactory() {
    private static final long serialVersionUID = 1L;

    @Override
    public Field<?> createField(Container container, Object itemId, Object propertyId, com.vaadin.ui.Component uiContext) {

        TextField tField = (TextField) DefaultFieldFactory.get().createField(container, itemId, propertyId, uiContext);
        tField.setImmediate(true);

        if (propertyId.equals("Foo")) {
            // field.setImmediate(true); //tried variations of this
            // field.setBuffered(false); //tried variations of this
            return tField;
        }
        else {
            tField.setReadOnly(true);
        }
        return tField;
    }
});
Roger
  • 2,684
  • 4
  • 36
  • 51

1 Answers1

1

By keeping track of the fields created in the factory (and making those fields buffered), you can then Commit/Discard as you wish.

I've created a simple example of buffered table editing in this self-contained GitHub Gist. Select a row and click Edit (or double click). Make changes, and click Save/Hit Enter to commit, or Cancel/Escape to discard.

I've deliberately made only one row-at-a-time editable, because frankly that's the only thing that makes any sense to me. Obviously, that's easily changed.

Charles Anthony
  • 3,155
  • 17
  • 21
  • I followed your examples with the attachListeners and it works like a charm! Thank you, Charles! – Roger Sep 24 '13 at 10:47
  • I'm trying to figure out exactly how the attachListeners work, but I have a hard time finding any good material in the API or even when googling. If I understand correctly it would be something like this: 1. attach- and detachListeners are added to the field 2. when the field is attached to the table, a "connector field" (equivalent to reference?) is added to the fields list 3. when calling commit() on a "field reference" in the fields list, the field will be committed (or discarded if discard was called) I would be really grateful if you could confirm this! – Roger Sep 24 '13 at 10:49
  • The attach/detach listeners are simply called when a component is attached to/detached from the DOM. A Component/Field is a type of Connector. So - I'm simply keeping track of the fields as they are attached/detached. The commit/discard button just commits/discards all of the attached fields. – Charles Anthony Sep 24 '13 at 11:31