Vaadin 8.5.1-行更新后刷新网格

Vaadin 8.5.1- Refresh grid after row update

我正在使用 Vaadin 8.5.1 Grid 来显示 1000 行。 一旦一行被更新 属性,我使用 grid.getDataProvider().refreshItem(selectedRow) 要么 grid.getDataProvider().refreshAll() 无法更新行。

我需要显式 grid.setItems() 才能看到更新后的 属性 行。

我正在使用下面的代码片段创建一个网格

    msgGrid = new ABSMsgGrid();

    List<ConsoleEntry> messageEntryList = new ArrayList<>();
    if (inputConsole != null) {
        messageEntryList.addAll(inputConsole.getMessageEntryList());
    }

    msgGridDataProvider = new ListDataProvider<ConsoleEntry>(messageEntryList) {

        @Override
        public Object getId(ConsoleEntry item) {
            return item.getId();
        }
    };

    msgGrid.setDataProvider(msgGridDataProvider);



//on changing property of the grid row, i use the below snippet
private void handleHideRowMenuItem(GridContextMenu<ConsoleEntry> contextMenu, ConsoleEntry selectedConsoleItem) {
        if (!selectedConsoleItem.isHidden()) {
            hideRowMenuItem = contextMenu.addItem("Hide Row", VaadinIcons.EYE_SLASH, selectedMenuItem -> {
                    selectedConsoleItem.hide();
                    **msgGridDataProvider.refreshItem(selectedConsoleItem);**
                }
            });
        }
}

public class ConsoleEntry {

        @Override
        public boolean equals(Object obj) {
            // TODO Auto-generated method stub
            if (obj instanceof ConsoleEntry) {
                ConsoleEntry temp = (ConsoleEntry) obj;
                String msgRef2 = temp.getMsgRef();
                return this.getMsgRef().equalsIgnoreCase(msgRef2);
            }
            return false;
        }

        @Override
        public int hashCode() {
            // TODO Auto-generated method stub
            return super.hashCode();
        }

        public String getId(){
            return this.getMsgRef();
        }
}       

我见过类似的问题,但 none 的解决方案有效。

How to refresh the vaadin Grid after you change something?

如果有人可以分享有关如何解决此问题的建议,我们将不胜感激

TIA

要将一个项目视为同一项目(以及刷新工作),您需要在对象上正确实施 equals()hashCode() 方法。

来自文档

public void refreshItem(T​ item)

Description copied from interface: DataProvider

Refreshes the given item. This method should be used to inform all DataProviderListeners that an item has been updated or replaced with a new instance.

For this to work properly, the item must either implement

equals(​Object) and #hashCode() to consider both the old and the new item instances to be equal, or alternatively

DataProvider.​getId(​Object) should be implemented to return an appropriate identifier.

除此之外,您应该创建一个 ListDataProvider,将其分配给网格,然后通过先前分配的 ListDataProvider

的相同实例进行更新

作为 said, you must override your equals and hashCode 方法,它们考虑相同的字段,并且它们必须只考虑其值在您依赖它们的时间范围内不会改变的字段.

对于数据驱动的业务对象,这通常意味着简单地查看对象上的标识符,该标识符是您在数据库中的主键(或者如果您使用数据库,则可能是)。

如下例所示,我们的 Status class 有一个 UUID object as its identifier, a universally unique identifier (UUID)(128 位值,规范显示为带连字符的 36 个字符的十六进制字符串)。我们的 equalshashCode 只考虑对象的一个​​成员。

@Override
public boolean equals ( Object obj ) {  // Compare the UUID member named `uuid`.
    if ( obj == this ) { return true; }
    if ( obj instanceof Status ) { return this.getUuid().equals( ( ( Status ) obj ).getUuid() ); }
    return false;
}

@Override
public int hashCode () {
    return Objects.hashCode( this.uuid );
}  // Compare the UUID member named `uuid`.

示例应用程序

这是一个完整的工作示例。为 Vaadin 8.5 编写。

在这个例子中,我们跟踪了三台设备。 Status 对象代表每个。

我们修改当前状态编号(-1、0 或 1),方法是从弹出菜单中选择一个新的数值编号(Vaadin 中的 NativeSelect),然后单击哪个按钮的设备。选择一行在这里无关紧要。

为了您的启发,我写了两段不同的代码来更新 Grid 中的项目。这两种方法在 Vaadin 业务应用程序中都很常见:

  • 修改支持 Grid 的现有 Status 对象。
  • 用新实例替换目标 Status 对象,但在其 id 字段中使用相同的标识符值。

例如,在某些情况下,数据存储库服务代码可能总是写入 return 一个新实例。在其他情况下,我们可能会故意使用更改来更新现有对象。

    private void updateByModifying ( Status s , Integer integer ) {
        s.setCurrentStatus( integer );
        grid.getDataProvider().refreshItem( s );
    }

    private void updateByInstantiating ( Status s , Integer integer ) {
        boolean removed = statuses.remove( s );
        Status replacement = new Status( s.getUuid() , s.getName() , integer );
        boolean added = statuses.add( replacement );
        grid.getDataProvider().refreshItem( replacement );
    }

在此演示中,使用 Update row by 弹出菜单(也称为下拉列表)在这些方法之间切换。这两种方法都通过调用 DataProvider::refreshItem 来工作。请注意,这两种方法都取决于您的 equalshashCode 是否得到正确实施。

作为进一步的例子,我添加了一个 Refresh all 按钮来展示 DataProvider::refreshAll 方法的使用。

源代码

此示例应用中的三个 Java 文件:

  • MyUI.java(进入Vaadin)
  • Status.java(我们的业务对象,POJO)
  • StatusLayout(UI内容和业务逻辑)

MyUI.java

package com.basilbourque.example;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.*;

import java.util.List;
import java.util.UUID;

/**
 * This UI is the application entry point. A UI may either represent a browser window
 * (or tab) or some part of an HTML page where a Vaadin application is embedded.
 * <p>
 * The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be
 * overridden to add component to the user interface and initialize non-component functionality.
 */
@Theme ( "mytheme" )
public class MyUI extends UI {

    @Override
    protected void init ( VaadinRequest vaadinRequest ) {
        Layout layout = new StatusLayout();
        this.setContent( layout );
    }

    @WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
    @VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
    public static class MyUIServlet extends VaadinServlet {
    }
}

Status.java

package com.basilbourque.example;

import java.util.Objects;
import java.util.UUID;

public class Status {
    // Members.
    private UUID uuid;
    private String name;
    private Integer currentStatus;  // 1 = Good, 0 = okay, -1 = Bad.

    // Constructor.
    public Status ( UUID uuid , String name , Integer currentStatus ) {
        this.uuid = uuid;
        this.name = name;
        this.currentStatus = currentStatus;
    }

    // -----------|  Object  |-------------------------

    @Override
    public boolean equals ( Object obj ) {  // Compare the UUID member named `uuid`.
        if ( obj == this ) { return true; }
        if ( obj instanceof Status ) { return this.getUuid().equals( ( ( Status ) obj ).getUuid() ); }
        return false;
    }

    @Override
    public int hashCode () {
        return Objects.hashCode( this.uuid );
    }  // Compare the UUID member named `uuid`.

    @Override
    public String toString () {
        return "Status{ " +
        "uuid=" + uuid +
        " | name='" + name + '\'' +
        " | currentStatus=" + currentStatus +
        " }";
    }

    // -----------|  Accessors  |-----------------------------

    public UUID getUuid () {
        return uuid;
    }

    public void setUuid ( UUID uuid ) {
        this.uuid = uuid;
    }

    public String getName () {
        return name;
    }

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

    public Integer getCurrentStatus () {
        return currentStatus;
    }

    public void setCurrentStatus ( Integer currentStatus ) {
        this.currentStatus = currentStatus;
    }
}

StatusLayout.java

package com.basilbourque.example;

import com.vaadin.data.provider.ListDataProvider;
import com.vaadin.ui.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

public class StatusLayout extends VerticalLayout {
    // Members
    List< Status > statuses;
    Grid< Status > grid;

    final List< Integer > numbers = List.of( 1 , 0 , - 1 );
    NativeSelect< Integer > numberPopup;
    NativeSelect< Boolean > updatyByModifyingOrInstantiating;
    Button setPump, setCamera, setSensor, refreshAllButton;

    // Constructor
    public StatusLayout () {

        statuses = new ArrayList<>( 3 );
        statuses.add( new Status( UUID.fromString( "1c0d183e-c2ba-11e8-a355-529269fb1459" ) , "Pump" , numbers.get( 0 ) ) );
        statuses.add( new Status( UUID.fromString( "2490c74e-1aac-4d71-9a2c-880628dcfc28" ) , "Camera" , numbers.get( 1 ) ) );
        statuses.add( new Status( UUID.fromString( "6ae07414-f557-4a1e-a552-cb5ec5f48476" ) , "Sensor" , numbers.get( 2 ) ) );

        // Create a grid bound to the list
        grid = new Grid<>( Status.class );
        grid.setCaption( "Equipment Status" );
        grid.setItems( statuses );
//        grid.addColumn( Status :: getName ).setCaption( "Name" );
//        grid.addColumn( Status :: getCurrentStatus ).setCaption( "Status" );

        updatyByModifyingOrInstantiating = new NativeSelect<>( "Update row by: " , List.of( Boolean.TRUE , Boolean.FALSE ) );
        updatyByModifyingOrInstantiating.setValue( Boolean.TRUE );
        updatyByModifyingOrInstantiating.setItemCaptionGenerator( ( ItemCaptionGenerator< Boolean > ) item -> item ? "modifying" : "instantiating" );

        Label valueSetterLabel = new Label( "Set status:" );
        numberPopup = new NativeSelect<>();
        numberPopup.setItems( numbers );
        numberPopup.setValue( numbers.get( 1 ) );
//        numberPopup.setItemCaptionGenerator( item -> List.of( "Good" , "Okay" , "Bad" ).get( numbers.indexOf( item ) ) );  // Display words rather than the underlying number.

        // The `buttonClick` method below has logic depending on match between button name and `name` property on `Status` objects in grid.
        setPump = new Button( statuses.get( 0 ).getName() );  // Pump
        setPump.addClickListener( this :: buttonClick );
        setCamera = new Button( statuses.get( 1 ).getName() ); // Camera
        setCamera.addClickListener( this :: buttonClick );
        setSensor = new Button( statuses.get( 2 ).getName() );   // Sensor
        setSensor.addClickListener( this :: buttonClick );

        refreshAllButton = new Button( "Refresh all" );
        refreshAllButton.addClickListener( clickEvent -> grid.getDataProvider().refreshAll() );

        // Arrange
        grid.setWidth( 100 , Unit.PERCENTAGE );

        HorizontalLayout valueSetterBar = new HorizontalLayout();
        valueSetterBar.addComponents( valueSetterLabel , numberPopup , setPump , setCamera , setSensor );
        valueSetterBar.setComponentAlignment( valueSetterLabel , Alignment.MIDDLE_CENTER );
        valueSetterBar.setComponentAlignment( numberPopup , Alignment.MIDDLE_CENTER );
        valueSetterBar.setComponentAlignment( setPump , Alignment.MIDDLE_CENTER );
        valueSetterBar.setComponentAlignment( setCamera , Alignment.MIDDLE_CENTER );
        valueSetterBar.setComponentAlignment( setSensor , Alignment.MIDDLE_CENTER );

        addComponents( grid , updatyByModifyingOrInstantiating , valueSetterBar , refreshAllButton );
    }

    private void buttonClick ( Button.ClickEvent clickEvent ) {
        System.out.println( "TRACE - Setting " + clickEvent.getButton().getCaption() + " to " + this.numberPopup.getValue().toString() );
        // Find the `Status` object in the `List` whose name matches the name of the button clicked by user.
        Optional< Status > optionalStatus = statuses.stream().filter( status -> status.getName().equals( clickEvent.getButton().getCaption() ) ).findFirst();
        // We expect the matching `Status` to always be found. If not, throw exception.
        Status s = optionalStatus.orElseThrow( () -> new IllegalStateException( "Failed to find expected item in list of statuses: " + clickEvent.getButton().getCaption() ) );
        Integer valueToSet = this.numberPopup.getValue();
        // Set the `currentStatus` property on the `Status` object to the value of the selected popup menu item.
        // Try either updating by modifying existing row or by instantiating a new one.
        // Comment-out either of the next two lines.
        if(updatyByModifyingOrInstantiating.getValue().equals( Boolean.TRUE )) {
            this.updateByModifying( s , valueToSet );
        } else {
            this.updateByInstantiating( s , valueToSet );
        }
    }

    private void updateByModifying ( Status s , Integer integer ) {
        s.setCurrentStatus( integer );
        grid.getDataProvider().refreshItem( s );
    }

    private void updateByInstantiating ( Status s , Integer integer ) {
        boolean removed = statuses.remove( s );
        Status replacement = new Status( s.getUuid() , s.getName() , integer );
        boolean added = statuses.add( replacement );
        grid.getDataProvider().refreshItem( replacement );
    }
}