com.vaadin.flow.component.checkbox.CheckboxGroup Maven / Gradle / Ivy
/*
* Copyright 2000-2018 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.component.checkbox;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.vaadin.flow.component.HasSize;
import com.vaadin.flow.component.HasValidation;
import com.vaadin.flow.component.ItemLabelGenerator;
import com.vaadin.flow.data.binder.HasDataProvider;
import com.vaadin.flow.data.binder.HasItemsAndComponents;
import com.vaadin.flow.data.provider.DataChangeEvent;
import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.KeyMapper;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.data.selection.MultiSelect;
import com.vaadin.flow.data.selection.MultiSelectionEvent;
import com.vaadin.flow.data.selection.MultiSelectionListener;
import com.vaadin.flow.dom.PropertyChangeEvent;
import com.vaadin.flow.dom.PropertyChangeListener;
import com.vaadin.flow.function.SerializablePredicate;
import com.vaadin.flow.shared.Registration;
import elemental.json.Json;
import elemental.json.JsonArray;
/**
* Server-side component for the {@code vaadin-checkbox-group} element.
*
* CheckBoxGroup is a multiselection component where items are displayed as
* check boxes.
*
* @author Vaadin Ltd
*/
public class CheckboxGroup
extends GeneratedVaadinCheckboxGroup, Set>
implements HasItemsAndComponents, HasSize, HasValidation,
MultiSelect, T>, HasDataProvider {
private static final String VALUE = "value";
private final KeyMapper keyMapper = new KeyMapper<>(this::getItemId);
private DataProvider dataProvider = DataProvider.ofItems();
private boolean isReadOnly;
private SerializablePredicate itemEnabledProvider = item -> isEnabled();
private ItemLabelGenerator itemLabelGenerator = String::valueOf;
private final PropertyChangeListener validationListener = this::validateSelectionEnabledState;
private Registration validationRegistration;
private Registration dataProviderListenerRegistration;
private static class CheckBoxItem extends Checkbox
implements ItemComponent {
private final T item;
private CheckBoxItem(String id, T item) {
this.item = item;
getElement().setProperty(VALUE, id);
}
@Override
public T getItem() {
return item;
}
}
public CheckboxGroup() {
super(Collections.emptySet(), Collections.emptySet(), JsonArray.class,
CheckboxGroup::presentationToModel,
CheckboxGroup::modelToPresentation);
registerValidation();
}
@Override
public void setDataProvider(DataProvider dataProvider) {
this.dataProvider = dataProvider;
reset();
if (dataProviderListenerRegistration != null) {
dataProviderListenerRegistration.remove();
}
dataProviderListenerRegistration = dataProvider
.addDataProviderListener(event -> {
if (event instanceof DataChangeEvent.DataRefreshEvent) {
T otherItem = ((DataChangeEvent.DataRefreshEvent) event)
.getItem();
this.getCheckboxItems()
.filter(item -> Objects.equals(
getItemId(item.item),
getItemId(otherItem)))
.findFirst().ifPresent(this::updateCheckbox);
} else {
reset();
}
});
}
@Override
public void updateSelection(Set addedItems, Set removedItems) {
Set value = new HashSet<>(getValue());
value.addAll(addedItems);
value.removeAll(removedItems);
setValue(value);
}
/**
* Sets the value of this component. If the new value is not equal to the
* previous value, fires a value change event.
*
* The component doesn't accept {@code null} values. The value of a checkbox
* group without any selected items is an empty set. You can use the
* {@link #clear()} method to set the empty value.
*
* @param value
* the new value to set, not {@code null}
* @throws NullPointerException
* if value is {@code null}
*/
@Override
public void setValue(Set value) {
Objects.requireNonNull(value,
"Cannot set a null value to checkbox group. "
+ "Use the clear-method to reset the component's value to an empty set.");
super.setValue(value);
refreshCheckboxes();
}
@Override
public Set getSelectedItems() {
return getValue();
}
@Override
public Registration addSelectionListener(
MultiSelectionListener, T> listener) {
return addValueChangeListener(event -> listener
.selectionChange(new MultiSelectionEvent<>(this, this,
event.getOldValue(), event.isFromClient())));
}
/**
* Gets the data provider.
*
* @return the data provider, not {@code null}
*/
public DataProvider getDataProvider() {
return dataProvider;
}
@Override
public void onEnabledStateChanged(boolean enabled) {
if (isReadOnly()) {
setDisabled(true);
} else {
setDisabled(!enabled);
}
getCheckboxItems().forEach(this::updateEnabled);
}
@Override
public void setReadOnly(boolean readOnly) {
isReadOnly = readOnly;
if (isEnabled()) {
setDisabled(readOnly);
refreshCheckboxes();
}
}
@Override
public boolean isReadOnly() {
return isReadOnly;
}
/**
* Returns the item enabled predicate.
*
* @return the item enabled predicate
* @see #setItemEnabledProvider
*/
public SerializablePredicate getItemEnabledProvider() {
return itemEnabledProvider;
}
/**
* Sets the item enabled predicate for this checkbox group. The predicate is
* applied to each item to determine whether the item should be enabled
* ({@code true}) or disabled ({@code false}). Disabled items are displayed
* as grayed out and the user cannot select them. The default predicate
* always returns true (all the items are enabled).
*
* @param itemEnabledProvider
* the item enable predicate, not {@code null}
*/
public void setItemEnabledProvider(
SerializablePredicate itemEnabledProvider) {
this.itemEnabledProvider = Objects.requireNonNull(itemEnabledProvider);
refreshCheckboxes();
}
/**
* Sets the item label generator that is used to produce the strings shown
* in the checkbox group for each item. By default,
* {@link String#valueOf(Object)} is used.
*
* @param itemLabelGenerator
* the item label provider to use, not null
*/
public void setItemLabelGenerator(
ItemLabelGenerator itemLabelGenerator) {
Objects.requireNonNull(itemLabelGenerator,
"The item label generator can not be null");
this.itemLabelGenerator = itemLabelGenerator;
reset();
}
/**
* Gets the item label generator that is used to produce the strings shown
* in the checkbox group for each item.
*
* @return the item label generator used, not null
*/
public ItemLabelGenerator getItemLabelGenerator() {
return itemLabelGenerator;
}
@Override
public void setLabel(String label) {
super.setLabel(label);
}
/**
* Gets the label of the checkbox group.
*
* @return the {@code label} property of the checkbox group
*/
public String getLabel() {
return super.getLabelString();
}
@Override
public void setErrorMessage(String errorMessage) {
super.setErrorMessage(errorMessage);
}
/**
* Gets the current error message from the checkbox group.
*
* @return the current error message
*/
@Override
public String getErrorMessage() {
return getErrorMessageString();
}
@Override
public void setRequired(boolean required) {
super.setRequired(required);
}
/**
* Determines whether the checkbox group is marked as input required.
*
* This property is not synchronized automatically from the client side, so
* the returned value may not be the same as in client side.
*
* @return {@code true} if the input is required, {@code false} otherwise
*/
public boolean isRequired() {
return isRequiredBoolean();
}
@Override
public boolean isInvalid() {
return isInvalidBoolean();
}
@Override
public void setInvalid(boolean invalid) {
super.setInvalid(invalid);
}
@Override
protected boolean valueEquals(Set value1, Set value2) {
assert value1 != null && value2 != null;
if (value1.size() != value2.size()) {
return false;
}
if (getDataProvider() == null) {
return super.valueEquals(value1, value2);
}
Set