Hi, i am using this custom component for customer contacts
i have noticed that the binder does not update when adding / removing a contact.
So i have added updateValue(), but then it only ever updates the binder’s entity the first time
so it works if i only add / remove a single contact a time,
as soon as i want to add / remove multiple at once it does not work
ContactField contacts = new ContactField(getTranslation("bms.customer.contacts"));
binder.forField(contacts).bind(CustomerEntity::getContacts, CustomerEntity::setContacts);
package ch.orca.ui.component;
import ch.orca.data.test.entity.ContactEntity;
import ch.orca.util.ApplicationUtil;
import com.vaadin.flow.component.accordion.Accordion;
import com.vaadin.flow.component.accordion.AccordionPanel;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.customfield.CustomField;
import com.vaadin.flow.component.details.DetailsVariant;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridVariant;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.provider.ListDataProvider;
import org.jooq.generated.enums.ContactType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ContactField extends CustomField<List<ContactEntity>> {
private Button saveButton;
private enum EditMode { ADD, EDIT }
private EditMode editMode = EditMode.ADD;
private final List<ContactEntity> items = new ArrayList<>();
private final Binder<ContactEntity> binder = new Binder<>();
private final Grid<ContactEntity> grid = new Grid<>();
public ContactField(String label) {
add(buildAccordion(label));
}
private Accordion buildAccordion(String label) {
VerticalLayout layout = new VerticalLayout(buildGrid(), buildInputLayout());
layout.setPadding(false);
Accordion accordion = new Accordion();
AccordionPanel panel = accordion.add(label, layout);
panel.addThemeVariants(DetailsVariant.LUMO_FILLED, DetailsVariant.LUMO_SMALL);
binder.setBean(new ContactEntity());
return accordion;
}
private Grid<ContactEntity> buildGrid() {
grid.addThemeVariants(GridVariant.LUMO_COMPACT, GridVariant.LUMO_NO_BORDER, GridVariant.LUMO_NO_ROW_BORDERS);
grid.setSelectionMode(Grid.SelectionMode.NONE);
grid.setAllRowsVisible(true);
grid.setDataProvider(new ListDataProvider<>(items));
grid.addItemDoubleClickListener(event -> onEdit(event.getItem()));
grid.addColumn(contact -> getTranslation(enumerationKey(contact.getType())))
.setHeader(getTranslation("bms.contact.type")).setFlexGrow(0).setAutoWidth(true);
grid.addColumn(ContactEntity::getValue)
.setHeader(getTranslation("bms.contact.value"));
grid.addColumn(ContactEntity::getComment)
.setHeader(getTranslation("bms.contact.comment"));
grid.addComponentColumn(this::buildRowControls)
.setHeader(getTranslation("bms.common.controls"))
.setFlexGrow(0)
.setAutoWidth(true);
return grid;
}
private HorizontalLayout buildInputLayout() {
ComboBox<ContactType> type = new ComboBox<>(getTranslation("bms.contact.type"), ContactType.values());
type.setItemLabelGenerator(contactType -> getTranslation(enumerationKey(contactType)));
binder.bind(type, ContactEntity::getType, ContactEntity::setType);
TextField value = new TextField(getTranslation("bms.contact.value"));
binder.bind(value, ContactEntity::getValue, ContactEntity::setValue);
TextField comment = new TextField(getTranslation("bms.contact.comment"));
binder.bind(comment, ContactEntity::getComment, ContactEntity::setComment);
saveButton = new Button(VaadinIcon.PLUS.create(), event -> onSave(binder.getBean()));
saveButton.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_SUCCESS);
Button clearButton = new Button(VaadinIcon.REFRESH.create(), event -> onClear());
clearButton.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_WARNING);
HorizontalLayout buttons = new HorizontalLayout(saveButton, clearButton);
HorizontalLayout layout = new HorizontalLayout(type, value, comment, buttons);
layout.setAlignItems(FlexComponent.Alignment.END);
layout.setWidthFull();
layout.setFlexGrow(1, type, value, comment);
layout.setFlexGrow(0, buttons);
return layout;
}
private HorizontalLayout buildRowControls(ContactEntity contact) {
Button editButton = new Button(VaadinIcon.EDIT.create(), event -> onEdit(contact));
editButton.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_SMALL, ButtonVariant.LUMO_PRIMARY);
Button deleteButton = new Button(VaadinIcon.TRASH.create(), event -> onDelete(contact));
deleteButton.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_SMALL, ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_PRIMARY);
return new HorizontalLayout(editButton, deleteButton);
}
private String enumerationKey(ContactType contactType) {
return "bms.enumeration." + ApplicationUtil.toPropertyCase(contactType.getClass()) + "." + contactType.name().toLowerCase();
}
private void onSave(ContactEntity bean) {
if (editMode == EditMode.ADD) {
items.add(bean);
}
grid.getDataProvider().refreshAll();
binder.setBean(new ContactEntity());
setEditMode(EditMode.ADD);
updateValue();
}
private void onClear() {
binder.setBean(new ContactEntity());
setEditMode(EditMode.ADD);
updateValue();
}
private void onEdit(ContactEntity contact) {
binder.setBean(contact);
setEditMode(EditMode.EDIT);
updateValue();
}
private void onDelete(ContactEntity contact) {
items.remove(contact);
grid.getDataProvider().refreshAll();
binder.setBean(new ContactEntity());
updateValue();
}
private void setEditMode(EditMode editMode) {
this.editMode = editMode;
if (editMode == EditMode.ADD) {
saveButton.setIcon(VaadinIcon.PLUS.create());
} else {
saveButton.setIcon(VaadinIcon.EDIT.create());
}
}
@Override
protected List<ContactEntity> generateModelValue() {
return Collections.unmodifiableList(items);
}
@Override
protected void setPresentationValue(List<ContactEntity> contactEntities) {
items.clear();
items.addAll(contactEntities);
}
}