Browse Source

11642 refresh pagelength 0 combobox items after dataprovider update (#11653)

* Fixes #11642. ComboBox with pageLength 0 should be updated if DataProvider changes

* added comments, fixed imports
tags/8.10.0.alpha1
Olli Tietäväinen 4 years ago
parent
commit
901705e1c8

+ 16
- 1
client/src/main/java/com/vaadin/client/ui/combobox/ComboBoxConnector.java View File

@@ -62,6 +62,12 @@ public class ComboBoxConnector extends AbstractListingConnector
*/
private String pendingNewItemValue = null;

/**
* If this flag is toggled, even unpaged data sources should be updated on
* reset.
*/
private boolean forceDataSourceUpdate = false;

@Override
protected void init() {
super.init();
@@ -123,6 +129,11 @@ public class ComboBoxConnector extends AbstractListingConnector
getWidget().setEmptySelectionCaption(getState().emptySelectionCaption);
}

@OnStateChange("forceDataSourceUpdate")
private void onForceDataSourceUpdate() {
forceDataSourceUpdate = getState().forceDataSourceUpdate;
}

@OnStateChange({ "selectedItemKey", "selectedItemCaption",
"selectedItemIcon" })
private void onSelectionChange() {
@@ -503,9 +514,13 @@ public class ComboBoxConnector extends AbstractListingConnector
@Override
public void resetDataAndSize(int estimatedNewDataSize) {
if (getState().pageLength == 0) {
if (getWidget().suggestionPopup.isShowing()) {
if (getWidget().suggestionPopup.isShowing()
|| forceDataSourceUpdate) {
dataSource.ensureAvailability(0, estimatedNewDataSize);
}
if (forceDataSourceUpdate) {
rpc.resetForceDataSourceUpdate();
}
// else lets just wait till the popup is opened before
// everything is fetched to it. this could be optimized later on
// to fetch everything if in-memory data is used.

+ 26
- 5
server/src/main/java/com/vaadin/ui/ComboBox.java View File

@@ -28,17 +28,19 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;

import org.jsoup.nodes.Element;

import com.vaadin.data.HasFilterableDataProvider;
import com.vaadin.data.HasValue;
import com.vaadin.data.ValueProvider;
import com.vaadin.data.provider.CallbackDataProvider;
import com.vaadin.data.provider.DataChangeEvent;
import com.vaadin.data.provider.DataCommunicator;
import com.vaadin.data.provider.DataGenerator;
import com.vaadin.data.provider.DataKeyMapper;
import com.vaadin.data.provider.DataProvider;
import com.vaadin.data.provider.InMemoryDataProvider;
import com.vaadin.data.provider.ListDataProvider;
import org.jsoup.nodes.Element;

import com.vaadin.data.HasFilterableDataProvider;
import com.vaadin.data.HasValue;
import com.vaadin.data.ValueProvider;
import com.vaadin.event.FieldEvents;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
@@ -219,6 +221,11 @@ public class ComboBox<T> extends AbstractSingleSelect<T>
getState().currentFilterText = filterText;
filterSlot.accept(filterText);
}

@Override
public void resetForceDataSourceUpdate() {
getState().forceDataSourceUpdate = false;
}
};

/**
@@ -962,6 +969,20 @@ public class ComboBox<T> extends AbstractSingleSelect<T>

filterSlot = filter -> providerFilterSlot
.accept(convertOrNull.apply(filter));

// This workaround is done to fix issue #11642 for unpaged comboboxes.
// Data sources for on the client need to be updated after data provider
// refreshAll so that serverside selection works even before the dropdown
// is opened. Only done for in-memory data providers for performance
// reasons.
if (dataProvider instanceof InMemoryDataProvider) {
dataProvider.addDataProviderListener(event -> {
if ((!(event instanceof DataChangeEvent.DataRefreshEvent))
&& (getPageLength() == 0)) {
getState().forceDataSourceUpdate = true;
}
});
}
}

/**

+ 7
- 0
shared/src/main/java/com/vaadin/shared/ui/combobox/ComboBoxServerRpc.java View File

@@ -40,4 +40,11 @@ public interface ComboBoxServerRpc extends ServerRpc {
* mode
*/
public void setFilter(String filter);

/**
* Reset the force update flag once the list contents have been updated.
*
* @since
*/
public void resetForceDataSourceUpdate();
}

+ 8
- 0
shared/src/main/java/com/vaadin/shared/ui/combobox/ComboBoxState.java View File

@@ -25,6 +25,7 @@ import com.vaadin.shared.ui.AbstractSingleSelectState;
* @since 7.0
*/
public class ComboBoxState extends AbstractSingleSelectState {

{
// TODO ideally this would be v-combobox, but that would affect a lot of
// themes
@@ -107,4 +108,11 @@ public class ComboBoxState extends AbstractSingleSelectState {
*/
public String currentFilterText;

/**
* Ensure the data source is updated when backing dataprovider has been
* refreshed.
*
* @since
*/
public boolean forceDataSourceUpdate;
}

+ 116
- 0
uitest/src/main/java/com/vaadin/tests/components/combobox/ComboBoxPageLengthZeroRefreshItemAfterDataProviderUpdate.java View File

@@ -0,0 +1,116 @@
package com.vaadin.tests.components.combobox;

import java.util.ArrayList;
import java.util.Objects;

import com.vaadin.annotations.Widgetset;
import com.vaadin.data.provider.ListDataProvider;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.VerticalLayout;

@Widgetset("com.vaadin.DefaultWidgetSet")
public class ComboBoxPageLengthZeroRefreshItemAfterDataProviderUpdate
extends AbstractTestUI {

private static final class Bean {
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Bean bean = (Bean) o;
return Objects.equals(name, bean.name);
}

@Override
public int hashCode() {
return Objects.hash(name);
}

private String name;

public Bean(String name) {
setName(name);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

private ArrayList<Bean> list = new ArrayList<>();
private ArrayList<Bean> list2 = new ArrayList<>();

private int counter = 0;

@Override
protected void setup(VaadinRequest request) {
HorizontalLayout root = new HorizontalLayout();
root.setSizeFull();
VerticalLayout leftLayout = new VerticalLayout();
VerticalLayout rightLayout = new VerticalLayout();
root.addComponent(leftLayout);
root.addComponent(rightLayout);
addComponent(root);
final ComboBox<Bean> comboBoxPageLengthZero = new ComboBox<>();
comboBoxPageLengthZero.setId("combo-0");
comboBoxPageLengthZero.setPageLength(0);
final ComboBox<Bean> comboBoxRegular = new ComboBox<>();
comboBoxRegular.setId("combo-n");
comboBoxPageLengthZero.setItemCaptionGenerator(bean -> bean.getName());
comboBoxRegular.setItemCaptionGenerator(bean -> bean.getName());
ListDataProvider<Bean> dataProvider = new ListDataProvider<>(list);
ListDataProvider<Bean> dataProvider2 = new ListDataProvider<>(list2);
comboBoxPageLengthZero.setDataProvider(dataProvider);
comboBoxRegular.setDataProvider(dataProvider2);
createComponents(leftLayout, comboBoxPageLengthZero, dataProvider,
true);
createComponents(rightLayout, comboBoxRegular, dataProvider2, false);
}

private void createComponents(VerticalLayout layout,
ComboBox<Bean> comboBox, ListDataProvider<Bean> dataProvider,
boolean pageLengthZero) {
Button updateValues = new Button(
"1. Refresh backing list and dataprovider, select first item",
e -> {
Bean selected = null;
if (pageLengthZero) {
list.clear();
list.add(new Bean("foo" + (++counter)));
list.add(new Bean("foo" + (++counter)));
selected = list.get(0);
} else {
list2.clear();
list2.add(new Bean("bar" + (++counter)));
list2.add(new Bean("bar" + (++counter)));
selected = list2.get(0);
}
dataProvider.refreshAll();
comboBox.setValue(selected);
});

updateValues.setId(pageLengthZero ? "update-0" : "update-n");

Button refresh = new Button(
"2. Update item returned by combobox's getValue and call refreshItem on data provider",
e -> {
Bean currentValue = comboBox.getValue();
currentValue.setName(currentValue.getName() + "(updated)");
dataProvider.refreshItem(currentValue);
});
refresh.setId(pageLengthZero ? "refresh-0" : "refresh-n");

layout.addComponents(updateValues, refresh, comboBox);
}

}

+ 50
- 0
uitest/src/test/java/com/vaadin/tests/components/combobox/ComboBoxPageLengthZeroRefreshItemAfterDataProviderUpdateTest.java View File

@@ -0,0 +1,50 @@
package com.vaadin.tests.components.combobox;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

import com.vaadin.testbench.elements.ButtonElement;
import com.vaadin.testbench.elements.ComboBoxElement;
import com.vaadin.tests.tb3.MultiBrowserTest;
import org.openqa.selenium.By;

public class ComboBoxPageLengthZeroRefreshItemAfterDataProviderUpdateTest
extends MultiBrowserTest {

@Test
public void refreshItemAfterDataProviderUpdateWithPageLengthZero() {
openTestURL();
waitForElementVisible(By.id("combo-0"));
ComboBoxElement pageLengthZeroCombo = $(ComboBoxElement.class)
.id("combo-0");
ButtonElement updateButton = $(ButtonElement.class).id("update-0");
ButtonElement refreshButton = $(ButtonElement.class).id("refresh-0");
String comboText = getComboBoxInputTextAfterUpdateAndRefresh(
pageLengthZeroCombo, updateButton, refreshButton);
assertEquals("Expected item containing (updated), got " + comboText,
true, comboText.contains("(updated)"));
}

@Test
public void refreshItemAfterDataProviderUpdateWithDefaultPageLength() {
openTestURL();
waitForElementVisible(By.id("combo-n"));
ComboBoxElement pageLengthRegularCombo = $(ComboBoxElement.class)
.id("combo-n");
ButtonElement updateButton = $(ButtonElement.class).id("update-n");
ButtonElement refreshButton = $(ButtonElement.class).id("refresh-n");
String comboText = getComboBoxInputTextAfterUpdateAndRefresh(
pageLengthRegularCombo, updateButton, refreshButton);
assertEquals("Expected item containing (updated), got " + comboText,
true, comboText.contains("(updated)"));
}

public String getComboBoxInputTextAfterUpdateAndRefresh(
ComboBoxElement combo, ButtonElement updateDataProvider,
ButtonElement refreshItem) {
updateDataProvider.click();
refreshItem.click();
return combo.getValue();
}
}

Loading…
Cancel
Save