How to refresh Grid?

Hello, everyone!

I have such simple code.

@PageTitle("grid")
@Route(value = "grid")
public class GridProblem extends VerticalLayout {


    private Grid<String> grid;
    private ListDataProvider<String> dataProvider = new ListDataProvider<>(new ArrayList<>());
    
    public GridProblem() {
	
	grid = new Grid<String>();
	grid.setSizeFull();
    	grid.setDataProvider(dataProvider);
    	grid.setHeight("400px");
    	grid.addColumn(r -> r).setTextAlign(ColumnTextAlign.END).setHeader("Value").setSortable(true);    	
    	add(grid);
    	

    	futureStream().forEach(future -> {
	    future.thenAccept(result -> {
		System.out.println(result);
		dataProvider.getItems().add(result);
		dataProvider.refreshAll();
		grid.setItems(dataProvider.getItems());
	    });

	});
    }
    
    public Stream<CompletableFuture<String>> futureStream() {
	List<CompletableFuture<String>> list = new ArrayList<>();
	for (int i = 0; i < 10; i++) {
	    list.add(CompletableFuture.supplyAsync(() -> {
		int nextInt = new Random().nextInt(3);
		try {
		    TimeUnit.SECONDS.sleep(nextInt);
		} catch (InterruptedException e) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
		return ""+nextInt;
	    }));
	}
	return list.stream();
    }
}

I want to refresh Grid every time I receive new Item.

But for some reason it doesn’t work as I expect.
It shows only few results if they are fast enough.
image

Also grid is refreshed if I change sorting order.

Can someone explain what i missed please?

image

After changing sorting

Completablefuture is async, so you need Push.

1 Like

And UI.access around the part that modifies the UI components. And I suggest to forget the DataProvider API as it just complicates things. Try this (with @Push as suggested by @knoobie ):

package org.vaadin.firitin;

import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.grid.ColumnTextAlign;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.router.Route;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

@Route
public class GridProblem extends VerticalLayout {

    private Grid<String> grid;
    private List<String> strings = new ArrayList<>();

    public GridProblem() {

        grid = new Grid<>();
        grid.setSizeFull();
        grid.setItems(strings);
        grid.setHeight("400px");
        grid.addColumn(r -> r).setTextAlign(ColumnTextAlign.END).setHeader("Value").setSortable(true);
        add(grid);

        UI ui = UI.getCurrent();

        futureStream().forEach(future -> {
            future.thenAccept(result -> {
                ui.access(() -> {
                    System.out.println(result);
                    strings.add(result);
                    grid.setItems(strings);
                });
            });

        });
    }

    public Stream<CompletableFuture<String>> futureStream() {
        List<CompletableFuture<String>> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(CompletableFuture.supplyAsync(() -> {
                int nextInt = new Random().nextInt(3);
                try {
                    TimeUnit.SECONDS.sleep(nextInt);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return "" + nextInt;
            }));
        }
        return list.stream();
    }
}
2 Likes

Thank you for fast answer! now I understand better vaadin)

Thank you for fixing my code!

For others who face same problem:
don’t forget to add vaadin.pushMode=AUTOMATIC to your application.properties

That should be the default and not needed at all.

For some reason grid isn’t updated if I remove this property despite that I have same Atmosphere logs in both cases. I use Vaadin 14

Anyway thanks a lot for your help. Without you and Christian Knoop i would spend a whole day(or even much more) for that!

That’s weird, the default should be automatic in Vaadin 14 too. Maybe it is defined in some other location to be “manual”, like directly in the Push annotation that you might have have somewhere.

I’m sorry to disturb you again.

My code doesn’t work as I expect again.

I have ResultForm with Grid component.
setResultList method simply does grid.setItems
exception comes from grid’s column value provider.

AtmospherePushConnection catches this exception and throws new one.
But I don’t see this exception in logs.

I’ve tried to add ErrorHandler, but probably in a wrong way.
Also I’ve tried this solution from Issues · vaadin/flow · GitHub

Can you give me some references.

Instead of providing a “Command” to the UI::access call, use the “ErrorHandlingCommand”, this allows to handle the error directly within your problematic code.

Thank you for answer.

I’ve tried your solution but it didn’t help.
image

In console I can see only “HEY”.

I suppose that problem is next: exception isn’t thrown by setResultList,
its thrown by subsequent table update in ValueProvider.

I wonder why the exception is eaten then :thinking: Could it be that everything actually works, but there is still an issue with Push configuration and the changes are just not reflected to the browser side? You could check if things start to work if you configure polling with e.g. UI.setPollInterval(1000).

Otherwise I’d check with debugger what is going on, but would need a reduced test case for that.

BTW. That getUI() method is a bit of a trap that many fall into (if it is the one from Component), and I’d even call it a design flaw. That is not thread safe, so you should call it only within properly synced call (some framework initiated call or within UI.access). So store the UI reference somewhere earlier and then use that reference instead the getUI() method. I don’t believe this to be the issue here though…

Hello, one more question about server pushes.

I have such code.
image

I want to add result rows to Grid asap. Also i’d like to keep loading indicator shown till all futures are completed.

If I comment out the line CompletableFuture.allOf(futures).join();
new values apears in the Grid as expected, but loading indicator is missing.
other way, with this line (CompletableFuture.allOf(futures).join()) new results won’t be added to Grid till all the futures are completed

I’ve checked in debug UI.push() is’nt called for some reason.

I understand that CompletableFuture.allOf(futures).join(); locks current thread to wait. Probably I should remove CompletableFuture.allOf(futures).join() and show loading indicator programmatically.

Do you know better ways to solve this?

You can get a inspiration from this How do I show a meaningful message instead of an empty Grid - Vaadin Cookbook

I personally apply a “loading” css by default before push and once push pushed its data I remove that class again.

Thanks for advice! I’ll try