Delete button on each row in an editable Grid?

I’ve recently added a set of “action” buttons to my Grid. Amongst others a Delete button.

I managed to hack together something that mostly worked:

  • Ensure that I have wrapped my Container in a GeneratedPropertyContainer
  • Add a generated property that returns the unicode of FontAwesome.TRASH
  • Set a CellStyleGenerator on my grid that returns “font-awesome” for this column
  • Set the font-family to FontAwesome for class “font-awesome” in my css
  • Set a ButtonRenderer for the generated column that on a click toggles my “row-status”.
  • Set a RowStyleGenerator on my grid that returns the css class for the current “row-status”.

This worked great, but then I made the list editable…

There is no support for having an “editor” for a generated property. Vaadin just displays the text instead.
( Even if there was support for it, a Button isn’t a Field, so I would not be allowed to add it anyway. )

Currently the text displays as just the “unknown character” square.
It is possible that I could style it to use the Font Awesome font, but that would actually make it worse, because then the users would try to click it and nothing would happen…

If I used the buffered editor, it would be acceptable to display nothing. The users would then have to close the editor before performing any of the actions.

However, we use the unbuffered editor, and can’t expect the users to know that they must “close” the editor by pressing escape. Ideally the users shouldn’t have to know that there is a special editor-mode.
The editor should be as transparent to the user as possible, meaning that the buttons that were available before the editor was opened should still be available inside the editor.

Anyone have any suggestions for how I could achieve this?

How about use own CustomFieldGroup with grid and let it listen “button clicks” from editor

grid.setEditorFieldGroup(new CustomFieldGroup(lister)); In there when you have “ButtonField” crated to the some propertyIds.

[code]
public class CustomFieldGroup extends FieldGroup {
private ButtonFieldClickListener listener;
public CustomFieldGroup(ButtonFieldClickListener listener) {
setFieldFactory(EditorFieldFactory.get());
this.listener = listener;
}

@Override
public Field<?> buildAndBind(Object propertyId) throws BindException {
if (???properyId???){
//create button field
ButtonField bf = new ButtonField(listener, propertyId);
bind(bf, propertyId);
return bf;
}
return super.buildAndBind(propertyId);
}
}
[/code]ButtonField can be something like this.
Of course you must style button field so that it looks same as button in the normal row.

public class ButtonField extends CustomField<String> {
  public interface ButtonFieldClickListener {
    void buttonClicked(Object propertyId, Property property);
  }

  private Button b = new Button();
  private Object propertyId;
  private ButtonFieldClickListener listener;

  public ButtonField(ButtonFieldClickListener listener, Object propertyId) {
    super();
    this.propertyId = propertyId;
    this.listener = listener;
  }

  @Override
  public Class<? extends String> getType() {
    return String.class;
  }
  @Override
  protected Component initContent() {
    b.addClickListener(e->this.listener.handleButtonField(this.propertyId, this.getWrappedProperty()));
    return b;
  }

  private Property getWrappedProperty() {
    Property p = this.getPropertyDataSource();
    if (p == null) {
      return null;
    }
    if (p.getClass() == TransactionalPropertyWrapper.class) {
      return((TransactionalPropertyWrapper) p).getWrappedProperty();
    }
    return null;
  }

Thanks for that detailed suggestion, Vesa.
It got me a bit farther along the way, but unfortunately not all the way.

With a custom FieldGroup I get the chance to generate stuff in the editor-row, for generated columns.
Using a CustomField I can render a Button.
So far so good.

The problem comes when I want to get a reference to the actual Item in my Container. (to mark it as deleted)

I tried to use getItemDataSource() from the custom FieldGroup.
That gives me a GeneratedPropertyContainer.GeneratedPropertyItem.
Aand… GeneratedPropertyItem doesn’t expose the underlying item… :frowning:

As a quick hack to see if it would work, I made my own copy of GeneratedPropertyContainer, made GeneratedPropertyItem public and gave it a getWrappedItem method.

Now in CustomFieldGroup.buildAndBind I can extract my underlying Item and pass it to the ButtonField.

It works, but I’ll admit it is not the most elegant solution.

The way how you planned to do it sounds quite big job.
if you have grid available somehow just ask from it what item is under edit

public void handleButtonField(Object propertyId, Property property){ Object editedItemId = grid.getEditedItemId() Item item = grid.getContainerDataSource().getItem(editedItemId); } If not or you need item in ButtonField why not just extend Grid and set item or itemId to the CustomFieldGroup directly?
Something like…

public class OwnGrid extends Grid{
...
  //If you need hook to the itemId
  @Override
  public void editItem(Object itemId) throws IllegalStateException,
            IllegalArgumentException {
    if(this.editorFieldGroup.getClass() == CustomFieldGroup.class){
       ((CustomFieldGroup)this.editorFieldGroup).setEditItemId(itemId);
    }
    super.editItem(itemId);
  }
 //If you need hook to the item
 @Override
 protected void doEditItem() {
    if(this.editorFieldGroup.getClass() == CustomFieldGroup.class){
       ((CustomFieldGroup)this.editorFieldGroup).setEditItem(getContainerDataSource().getItem(editedItemId));
    }
    super.doEditItem();
 }

Thanks again Vesa, that was a much nicer hack than mine