Vaadin 8.5.1- Обновление сетки после обновления строки

Я использую 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();
        }
}       

Я видел подобный вопрос, но ни одно из решений не сработало.

Как обновить vaadin Grid после того, как вы что-то изменили?

Vaadin - обновить сетку после модификации строки

Спасибо, если кто-нибудь может поделиться указаниями о том, как решить эту проблему

ТИА

2 ответа

Решение

Чтобы элемент рассматривался как один и тот же элемент (и работает обновление), вам необходимо правильно реализовать equals() а также hashCode() методы на объекте.

Из документации

public void refreshItem(элемент T)

Описание скопировано с интерфейса: DataProvider

Обновляет данный предмет. Этот метод должен использоваться для информирования всех DataProviderListeners о том, что элемент был обновлен или заменен новым экземпляром.

Чтобы это работало должным образом, элемент должен

equals(Object) и #hashCode(), чтобы считать оба экземпляра старого и нового элемента равными или, альтернативно

DataProvider. GetId (Object) должен быть реализован для возврата соответствующего идентификатора.

В дополнение к этому, это вы должны создать ListDataProviderназначьте его сетке, а затем выполните обновление через тот же экземпляр ранее назначенного ListDataProvider

Как сказал правильный ответ Шильда, вы должны переопределить equals а также hashCode методы так далеко, что они рассматривают одно и то же поле (поля), и они должны учитывать только те поля, значения которых не будут изменяться в течение периода времени, когда вы зависите от них.

Для бизнес-объектов, управляемых данными, это обычно означает просто просмотр идентификатора объекта, который является вашим первичным ключом в базе данных (или был бы, если бы вы использовали базу данных).

Как видно из приведенного ниже примера, наш Status класс имеет UUID object как его идентификатор, универсальный уникальный идентификатор (UUID) (128-битное значение, канонически отображаемое в виде шестнадцатеричной строки из 36 символов с дефисами). наш equals а также hashCode считает, что только один член объекта.

@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`.

Пример приложения

Вот полный рабочий пример. Написано для Ваадина 8.5.

введите описание изображения здесь

В этом примере мы отслеживаем три единицы оборудования. Status объект представляет каждого.

Мы изменяем текущий номер состояния (-1, 0 или 1), выбирая новый номер значения во всплывающем меню (a NativeSelect в Vaadin), и нажав кнопку для какой части оборудования. Выбор строки здесь не имеет значения.

Для вашего написания я написал два разных куска кода для обновления элемента в Grid, Оба подхода распространены в бизнес-приложениях Vaadin:

  • Модификация существующего Status объект поддержки Grid,
  • Заменить цель Status объект со свежим экземпляром, но с тем же значением идентификатора в поле id.

Например, в некоторых случаях код службы хранилища данных может быть написан так, чтобы он всегда возвращал свежий экземпляр. В других случаях мы можем намеренно обновлять существующий объект с изменениями.

    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, Обратите внимание, что оба эти подхода зависят от вашего equals а также hashCode будучи должным образом реализованным.

В качестве дополнительного примера я добавил Refresh all кнопка, чтобы показать использование DataProvider::refreshAll метод.

Исходный код

Три Java-файла в этом примере приложения:

  • MyUI.java (вход в Ваадин)
  • Status.java (наш бизнес-объект, POJO)
  • StatusLayout (пользовательский интерфейс и бизнес-логика)

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 );
    }
}
Другие вопросы по тегам