All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.vaadin.viritin.fields.ElementCollectionField Maven / Gradle / Ivy

package org.vaadin.viritin.fields;

import com.vaadin.server.FontAwesome;
import com.vaadin.ui.AbstractField;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.Field;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.themes.ValoTheme;
import org.vaadin.viritin.MBeanFieldGroup;
import org.vaadin.viritin.button.ConfirmButton;
import org.vaadin.viritin.button.MButton;
import org.vaadin.viritin.form.AbstractForm;
import org.vaadin.viritin.layouts.MGridLayout;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A field suitable for editing collection of referenced objects tied to parent
 * object only. E.g. OneToMany/ElementCollection fields in JPA world.
 * 

* Some features/restrictions: *

    *
  • The field is valid when all elements are valid. *
  • The field is always non buffered *
  • The element type needs to have an empty parameter constructor or user * must provide an Instantiator. *
* * Elements in the edited collection are modified with BeanFieldGroup. Fields * should defined in a class. A simple usage example for editing * List>Address< adresses: *

 *  public static class AddressRow {
 *      EnumSelect type = new EnumSelect();
 *      MTextField street = new MTextField();
 *      MTextField city = new MTextField();
 *      MTextField zipCode = new MTextField();
 *  }
 *
 *  public static class PersonForm<Person> extends AbstractForm {
 *      private final ElementCollectionField<Address> addresses
 *              = new ElementCollectionField<Address>(Address.class,
 *                      AddressRow.class).withCaption("Addressess");
 *
 * 
* *

* Components in row model class don't need to match properties in the edited * entity. So you can add "custom columns" just by introducing them in your * editor row. *

* By default the field always contains an empty instance to create new rows. If * instances are added with some other method (or UI shouldn't add them at all), * you can configure this with setAllowNewItems. Deletions can be configured * with setAllowRemovingItems. *

* If developer needs to do some additional logic during element * addition/removal, one can subscribe to related events using * addElementAddedListener/addElementRemovedListener. * * * @author Matti Tahvonen * @param The type in the entity collection. The type must have empty * parameter constructor or you have to provide Instantiator. * */ public class ElementCollectionField extends AbstractElementCollection { private static final long serialVersionUID = 8573373104105052804L; List items = new ArrayList<>(); boolean inited = false; MGridLayout layout = new MGridLayout(); private boolean visibleHeaders = true; private boolean requireVerificationForRemoval; private AbstractForm popupEditor; public ElementCollectionField(Class elementType, Class formType) { super(elementType, formType); } public ElementCollectionField(Class elementType, Instantiator i, Class formType) { super(elementType, i, formType); } @Override public void addInternalElement(final ET v) { ensureInited(); items.add(v); MBeanFieldGroup fg = getFieldGroupFor(v); for (Object property : getVisibleProperties()) { Component c = fg.getField(property); if (c == null) { c = getComponentFor(v, property.toString()); Logger.getLogger(ElementCollectionField.class.getName()) .log(Level.WARNING, "No editor field for{0}", property); } layout.addComponent(c); layout.setComponentAlignment(c, Alignment.MIDDLE_LEFT); } if (getPopupEditor() != null) { MButton b = new MButton(FontAwesome.EDIT) .withStyleName(ValoTheme.BUTTON_ICON_ONLY) .withListener(new Button.ClickListener() { private static final long serialVersionUID = 5019806363620874205L; @Override public void buttonClick(Button.ClickEvent event) { editInPopup(v); } }); layout.add(b); } if (isAllowRemovingItems()) { layout.add(createRemoveButton(v)); } if (!isAllowEditItems()) { fg.setReadOnly(true); } } protected Component createRemoveButton(final ET v) { MButton b; if (requireVerificationForRemoval) { b = new ConfirmButton(); } else { b = new MButton(); } b.withIcon(FontAwesome.TRASH_O) .withStyleName(ValoTheme.BUTTON_ICON_ONLY, ValoTheme.BUTTON_DANGER) .withListener(new Button.ClickListener() { private static final long serialVersionUID = 5019806363620874205L; @Override public void buttonClick(Button.ClickEvent event) { removeElement(v); } }); return b; } @Override public void removeInternalElement(ET v) { int index = itemsIdentityIndexOf(v); items.remove(index); int row = index + 1; layout.removeRow(row); } @Override public GridLayout getLayout() { return layout; } @Override public void setPersisted(ET v, boolean persisted) { int row = itemsIdentityIndexOf(v) + 1; if (isAllowRemovingItems()) { Button c = (Button) layout.getComponent(layout.getColumns() - 1, row); if (persisted) { c.setDescription(getDeleteElementDescription()); } else { for (int i = 0; i < getVisibleProperties().size(); i++) { try { AbstractField f = (AbstractField) (Field) layout. getComponent(i, row); f.setValidationVisible(false); } catch (Exception e) { } } c.setDescription(getDisabledDeleteElementDescription()); } c.setEnabled(persisted); } } private int itemsIdentityIndexOf(Object o) { for (int index = 0; index < items.size(); index++) { if (items.get(index) == o) { return index; } } return -1; } private void ensureInited() { if (!inited) { int columns = getVisibleProperties().size(); if (isAllowRemovingItems()) { columns++; } if(getPopupEditor() != null) { columns++; } layout.setColumns(columns); if (visibleHeaders) { for (Object property : getVisibleProperties()) { Component header = createHeader(property); layout.addComponent(header); } if (isAllowRemovingItems()) { // leave last header slot empty, "actions" colunn layout.newLine(); } } inited = true; } } /** * Creates the header for given property. By default a simple Label is used. * Override this method to style it or to replace it with something more * complex. * * @param property the property for which header is to be created. * @return the component used for header */ protected Component createHeader(Object property) { Label header = new Label(getPropertyHeader(property. toString())); header.setWidthUndefined(); return header; } public ElementCollectionField withEditorInstantiator( Instantiator instantiator) { setEditorInstantiator(instantiator); return this; } public ElementCollectionField withNewEditorInstantiator( EditorInstantiator instantiator) { setNewEditorInstantiator(instantiator); return this; } public ElementCollectionField withVisibleHeaders(boolean visibleHeaders) { this.visibleHeaders = visibleHeaders; return this; } @Override public void clear() { if (inited) { items.clear(); int rows = inited ? 1 : 0; while (layout.getRows() > rows) { layout.removeRow(rows); } } } public String getDisabledDeleteElementDescription() { return disabledDeleteThisElementDescription; } public void setDisabledDeleteThisElementDescription( String disabledDeleteThisElementDescription) { this.disabledDeleteThisElementDescription = disabledDeleteThisElementDescription; } private String disabledDeleteThisElementDescription = "Fill this row to add a new element, currently ignored"; public String getDeleteElementDescription() { return deleteThisElementDescription; } private String deleteThisElementDescription = "Delete this element"; public void setDeleteThisElementDescription( String deleteThisElementDescription) { this.deleteThisElementDescription = deleteThisElementDescription; } @Override public void onElementAdded() { if (isAllowNewItems()) { newInstance = createInstance(); addInternalElement(newInstance); setPersisted(newInstance, false); } } @Override public ElementCollectionField setPropertyHeader(String propertyName, String propertyHeader) { super.setPropertyHeader(propertyName, propertyHeader); return this; } @Override public ElementCollectionField setVisibleProperties( List properties, List propertyHeaders) { super.setVisibleProperties(properties, propertyHeaders); return this; } @Override public ElementCollectionField setVisibleProperties( List properties) { super.setVisibleProperties(properties); return this; } @Override public ElementCollectionField setAllowNewElements( boolean allowNewItems) { super.setAllowNewElements(allowNewItems); return this; } @Override public ElementCollectionField setAllowRemovingItems( boolean allowRemovingItems) { super.setAllowRemovingItems(allowRemovingItems); return this; } @Override public ElementCollectionField withCaption(String caption) { super.withCaption(caption); return this; } @Override public ElementCollectionField removeElementRemovedListener( ElementRemovedListener listener) { super.removeElementRemovedListener(listener); return this; } @Override public ElementCollectionField addElementRemovedListener( ElementRemovedListener listener) { super.addElementRemovedListener(listener); return this; } @Override public ElementCollectionField removeElementAddedListener( ElementAddedListener listener) { super.removeElementAddedListener(listener); return this; } @Override public ElementCollectionField addElementAddedListener( ElementAddedListener listener) { super.addElementAddedListener(listener); return this; } /** * Expands the column with given property id * * @param propertyId the id of column that should be expanded in the UI * @return the element collection field */ public ElementCollectionField expand(String... propertyId) { for (String propertyId1 : propertyId) { int index = getVisibleProperties().indexOf(propertyId1); if (index == -1) { throw new IllegalArgumentException( "The expanded property must available"); } layout.setColumnExpandRatio(index, 1); } if (layout.getWidth() == -1) { layout.setWidth(100, Unit.PERCENTAGE); } // TODO should also make width of elements automatically 100%, both // existing and added, now obsolete config needed for row model return this; } public ElementCollectionField withFullWidth() { setWidth(100, Unit.PERCENTAGE); return this; } public ElementCollectionField withId(String id) { setId(id); return this; } public ElementCollectionField setRequireVerificationForRemoving(boolean requireVerification) { requireVerificationForRemoval = requireVerification; return this; } public AbstractForm getPopupEditor() { return popupEditor; } /** * Method to set form to allow editing more properties than it would be * convenient inline. * * @param newPopupEditor the popup editor to be used to edit instances */ public void setPopupEditor(AbstractForm newPopupEditor) { this.popupEditor = newPopupEditor; if (newPopupEditor != null) { newPopupEditor.setSavedHandler(new AbstractForm.SavedHandler() { private static final long serialVersionUID = 389618696563816566L; @Override public void onSave(ET entity) { MBeanFieldGroup fg = getFieldGroupFor(entity); fg.setItemDataSource(entity); fg.setBeanModified(true); // TODO refresh binding popupEditor.getPopup().close(); } }); } } /** * Opens a (possibly configured) popup editor to edit given entity. * * @param entity the entity to be edited */ public void editInPopup(ET entity) { getPopupEditor().setEntity(entity); getPopupEditor().openInModalPopup(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy