Important Notice - Forums is archived

To simplify things and help our users to be more productive, we have archived the current forum and focus our efforts on helping developers on Stack Overflow. You can post new questions on Stack Overflow or join our Discord channel.

Product icon

Vaadin lets you build secure, UX-first PWAs entirely in Java.
Free ebook & tutorial.

Vaadin 7.6.0 Grid editor save/cancel buttons doesn't work on second edit

Johannes Nilsson
7 years ago Jan 12, 2016 4:00pm
Fluffy Sandals
7 years ago Jan 12, 2016 8:47pm
Aaron Beverley
7 years ago Jan 13, 2016 5:23am
Fluffy Sandals
7 years ago Jan 13, 2016 7:47am
Johannes Nilsson
7 years ago Jan 13, 2016 8:24am
Fluffy Sandals
7 years ago Jan 13, 2016 8:29am
Johannes Nilsson
7 years ago Jan 13, 2016 9:01am
Johannes Nilsson
7 years ago Jan 14, 2016 9:55am
Teemu Suo-Anttila
7 years ago Jan 14, 2016 11:20am
Johannes Tuikkala
7 years ago Jan 14, 2016 11:54am
Johannes Nilsson
7 years ago Jan 14, 2016 12:29pm
Fluffy Sandals
7 years ago Jan 14, 2016 1:06pm
Limak Kyczbos
6 years ago Jul 28, 2016 8:22am
Lynn Pye
6 years ago Aug 29, 2016 2:00pm

I have also run into this problem. It appears to be related to performing an action during the Grid's save phase that tells the container to trigger an ItemSetChangeEvent. This appears to tell the Grid to invalidate the editor as it senses the underlying data has changed while the editor was up. In the case of the SQLContainer if you want to be able to commit in response to editing a row and having clicked the Grid editor's Save button, you have to do something to delay the firing of the SQLContainer's ItemSetChangeEvent notification in response to the commit() until you are outside the save handling logic of the Grid.

In my case, I have a Grid and a SQLContainer using a TableQuery pointing to an Oracle table. I set it up normally. I then tried two approaches in order to be notified that the Grid editor has completed a save:
- Add a CommitHandler to the EditorFieldGroup and perform SQLContainer.commit() in the postCommit method
- Override Grid.saveEditor(), calling super.saveEditor() followed by SQLContainer.commit()

In both cases, the save successfully commits all the way to the database on the first click of 'Save' but subsequent attempts to edit will open the editor with essentially non-responsive 'Save' and 'Cancel' buttons.

In the implementation of Grid, the call to initGrid() sets up an anonymous class implementing EditorServerRpc. The bind() method will get called in response to the editor being opened, while the save() method will be called in response to the editor's 'Save' button being clicked.

The bind() implementation, among other things, calls Grid.doEditItem() which does two things:
- sets editorActive to true
- checks to see if the container is an ItemSetChangeNotifier and if so adds "Grid.editorClosingItemSetListener" as an ItemSetChangeListener to the container
While the Grid.editorClosingItemSetListener is attached to the container, any operation which results in the container sending an ItemSetChangeEvent will invoke the "Grid.editorClosingItemSetListener.containerItemSetChange()" method. This method calls Grid.cancelEditor().

With regard to the save() method, it first invokes saveEditor() and then afterward calls "getEditorRpc().confirmSave(...)" to tell the client that the save succeeded. This is important because it means everything that happens in the call to Grid.saveEditor() must complete before the client is told things worked.

Grid.cancelEditor() does the following:
- calls getEditorRpc().cancel() to tell the client to cancel the editor
- calls Grid.doCancelEditor() ...
- ... which sets editorActive to false
- ... clears the field groups ItemDataSource (i.e. editorFieldGroup.setItemDataSource(null))
- ... removes the Grid.editorClosingItemSetListener
- ... marks the Grid as dirty

Grid.saveEditor() simply calls editorFieldGroup.commit().

In FieldGroup.commit(), the following sequence occurs (ignoring possible errors and validation issues):
- startTransactions()
- firePreCommitEvent() - your CommitHandler.preCommit() gets called here
- commitFields()
- firePostCommitEvent() - your CommitHandler.postCommit() gets called here
- commitTransactions()

In my case this results in the following sequence of events:
- User opens editor, makes a change to a field, and clicks 'Save'
​- Grid's EditorServerRpc.save() is called
- Grid.EditorServerRpc.save() calls saveEditor()
- Grid.saveEditor() calls FieldGroup.commit()
- FieldGroup.commit() eventually calls firePostCommitEvent()
- *** Problems begin here
- My CommitHandler.postCommit() gets called
- I call SQLContainer.commit()
- Data saved to DB, SQLContainer sends ItemSetChangeEvent to listeners
- Grid.editorClosingItemSetListener gets invoked
- Grid.cancelEditor() gets called
- Client gets notice that grid editor has been cancelled
- editorFieldGroup item data source is set to null and the editor is marked as inactive
- *** at this point, SQLContainer.commit() has completed and control returns to my CommitHandler.postCommit()
- return from CommitHandler.postCommit()
- FieldGroup.commit() calls commitTransactions()
- Attempts to iterate the Transactional Properties, fetch the Item data source of each and call commit
- Gets a NullPointerException because the item data source has been set to null

If I attempt to sub-class Grid and override saveEditor(), the behavior changes in that I no longer get the NullPointerException but the 'Save' and 'Cancel' buttons are left in a weird state where they are still locked but no error is thrown.

If I sub-class SQLContainer and add code to temporarily suspend notifications, and then wait until afterward (i.e. make the user click a button to manually trigger a SQLContainer.commit()), things work exactly as expected, with no errors, though this is not how I would expect the functionality to be implemented.

In my opinion I think the call to any post-commit hooks needs to come after the save confirmation has been sent to the client i.e. Grid.EditorServerRpc.save() would somehow be altered to call the commit hooks after the call to getEditorrpc().confirmSave(...). I understand that the commit may need to be cancelled in response to an error in the post-commit hook so then perhaps the ability to suspend listening/responding to container updates should be setup so that a user can actually commit to their container in the FieldGroup's post-commit handlers.

Lynn Pye
6 years ago Aug 30, 2016 1:45pm
Fluffy Sandals
6 years ago Aug 30, 2016 2:23pm
Lynn Pye
6 years ago Aug 30, 2016 2:52pm
Matti Tahvonen
6 years ago Sep 02, 2016 10:26am

That clearly looks like a bug to me and pretty critical as so many people have faced this. The ItemSetChangeEvent handling should be postponed while executing saveEditor.

I asked one of our Grid experts to look into this.


Freddy Boisseau
5 years ago Jul 20, 2017 7:51pm