While trying to use GWT's ListEditor
system I was unable to find a working example where the UI of each item in the list had a delete/remove button.
The examples I found were all like this one[1] and have an EditorSource.create()
implementation creates the per-item Editor
and appears to wire up a handler to remove the item from the underlying list via listEditor.getList().remove(index)
.
However, the anonymous implementation of the deletion handler closes around the value of index at the time of the sub-editor's creation, this leads to IndexOutOfBoundExceptions
or the wrong item being removed as each removal changes the index of all items that come after it.
I pulled my hair out for a while trying to see what I was missing in the examples that kept that from happening, but from what I could tell they really did all have that problem, so while the fix is fairly simple, I will still post it here so there is at least one example people can find that does item removal correctly.
[1] I think all the examples I found were all derived from the one I linked, although that one in particular had a little more logic in remove() and may have been doing something to avoid the problem like correcting the list order somehow, I haven't dug into the other code in that project.
The following is a minimal ListEditor
example that corrects the problem found in other examples.
public abstract class FooEditor extends Composite implements Editor<Foo> {
Widget root; // Instantiated explicitly or through uibinder
// Implemented as one of uibinder+fields, fields, methods, or LeafValueEditor.set/getValue()
public FooEditor() {
initWidget(root);
}
// Used for brevity, could be any triggering mechanism, click handler, event handler, etc.
abstract void onDeleteClicked();
}
public class FooListEditor extends Composite implements IsEditor<ListEditor<Foo, FooEditor>> {
private class FooEditorSource extends EditorSource<FooEditor> {
@Override
public FooEditor create(int index) {
FooEditor subEditor = new FooEditor()
{
@Override
public void onDeleteClicked()
{
// =======================================================
//
// This fixes the problem present in other examples
// by determining the current index at the time of removal
//
// =======================================================
int currentIndex = listEditor.getEditors().indexOf(this);
listEditor.getList().remove(currentIndex);
}
};
setIndex(subEditor, index);
return subEditor;
}
@Override
public void dispose(FooEditor subEditor) {
subEditor.removeFromParent();
}
@Override
public void setIndex(FooEditor subEditor, int index) {
listPanel.insert(subEditor, index);
}
}
FlowPanel listPanel; // Instantiated explicitly or through uibinder
ListEditor<Foo, FooEditor> listEditor = ListEditor.of(new FooEditorSource());
public FooListEditor() {
initWidget(listPanel);
}
@Override
public ListEditor<Foo, FooEditor> asEditor() {
return listEditor;
}
}