Vaadin 14: multiple Grid in a same view

Hi everyone!

I would like to be able to insert multiple grid components on the same page.

I actually managed to have something similar in the figure, I create multiple grids, insert elements within each one, but I can’t delete the individual rows.

Does anyone have a working example they can share?

Can you delete individual rows if there’s only one grid in the same view?

no i can’t remove single item if thes is one or more grid in a view.
when i use:

  • remove(object)
  • removeIf(predicate)
    the boolean result is true, so i complete with
    grid.getDataProvider().refreshAll()
    but the element remain.

Ok so the “multiple grids” mentioned in the title are actually not relevant if the same problem also occurs in the simpler case with only a single grid. It’s in general more likely that someone will be able to help if the problem description is as simple as possible while still being relevant.

It would be easier to understand what goes on here if you could share relevant parts of the code, i.e. how the data provider is created and configured with the grid and the code that is run when the delete button is clicked.

Hello, I didn’t disappear, I tried to create a code skeleton to share here.

I have a main class in which I create the view that will contain all my elements, and one used for creating and managing the grid.
From an initial test this time everything seems to be working correctly.

I take this opportunity to ask if the approach used is correct or if I should operate differently.

Thank you

main class

public class MultiGridView extends VerticalLayout  {
	
	/*
	 * class variable
	 */
	private Map<Short, Set<CateneControllate>> gridItemsMap = new HashMap<>(); //contain items that i put in a new Grid

	public class MultiGridView() {
		try {
			
			FormLayout form = new FormLayout();         

			/*
			 * BUTTON to create new Grid
			 */
			Button button = new Button("ADD new GRID");
			button.addClickListener(clickEvent -> {
				Short id = genIdAndPutInMap();		
				Grid newGrid = (new CustomGridCC(gridItemsMap.get(id), id)).grid;	
				form.add(newGrid);         
			});
			
			form.add(button); 
			
			add(form);
		 
        } catch (Exception e) {
            Notification.show("Error: "+e.getMessage());
        }   
	}

	/*
	 * class methods
	 */
	private Short genIdAndPutInMap(){
		Short id = 0;
		do {
			id = (short)(gridItemsMap.size()+1);
		}
		while (gridItemsMap.containsKey(id));
			gridItemsMap.put(id, new HashSet<>());			
		return id;
	}


}

creating and managing the grid

public class CustomGridCC extends Div {

    Grid<CustomObject> grid = new Grid<>(CustomObject.class, false);
    Editor<CustomObject> editor = grid.getEditor();
    Set<CustomObject> items = new HashSet<>();
    Short id = Short.valueOf("0");

    public CustomGridCC() {
    }
        
    public CustomGridCC(Set<CustomObject> inputItems, Short inputId) {
        
        items = inputItems;
        id = inputId;
        
        setupGrid();
        setupEditor();
        
    }

    public Grid<CustomObject> getGrid() {
        return grid;
    }
    
    
    
    private void setupGrid(){
        
        /*
         *
         */
        grid.addThemeVariants(GridVariant.LUMO_ROW_STRIPES, GridVariant.LUMO_COLUMN_BORDERS);
        grid.setWidthFull();        
        grid.setHeightByRows(true);
        grid.getStyle().set("margin-top", "10px");
        grid.setId(id.toString());
        grid.setItems(items);
            
        /*
         * BUTTON Add new row in grid
         */
        Button addRowbutton = new Button("Insert", VaadinIcon.PLUS.create());
        addRowbutton.setWidthFull();
        addRowbutton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        addRowbutton.setId(id.toString());
        addRowbutton.addClickListener(e -> { 

            CustomObject temp = new CustomObject();
			
			//A-mode
            items.add(temp);
            grid.getDataProvider().refreshAll();
            editor.editItem(temp);
			
			//B-mode
            //ListDataProvider<CustomObject> ldp = (ListDataProvider<CustomObject>)grid.getDataProvider();
            //ldp.getItems().add(temp);
            //ldp.refreshAll();
            //// workaround to refresh the keyMapper
            //grid.getDataCommunicator().getKeyMapper().key(temp);
            //grid.getEditor().editItem(temp);

        });   
        

        Grid.Column<CustomObject> c1Column = grid.addColumn(CustomObject::getC1)
                .setKey("c1Column")
                .setHeader("C1", Alignment.START))
                .setWidth("32%");
        
        Grid.Column<CustomObject> c2Column = grid.addColumn(CustomObject::getC2)
                .setKey("c2Column")
                .setHeader("C2", Alignment.START))
                .setWidth("16%");
        
        Grid.Column<CustomObject> c3Column = grid.addColumn(CustomObject::getC3)
                .setKey("c3Column")
                .setHeader("C3", Alignment.START))
                .setWidth("16%"); 
        
        Grid.Column<CustomObject> c4Column = grid.addColumn(CustomObject::getC4)
                .setKey("c4Column")
                .setHeader("C4", Alignment.START))
                .setWidth("16%");
                
        Grid.Column<CustomObject> editColumn = grid.addComponentColumn(item -> {
                            Button editButton = new Button(VaadinIcon.EDIT.create());
                            editButton.getElement().setProperty("title", "Edit");
                            editButton.addClassName("buttonFile");
                            editButton.addClickListener(e -> {                                                                
                                editor.editItem(item);
                            });
                            return editButton;
                        })
                .setKey("editColumn")
                .setHeader("Manage", Alignment.CENTER))
                .setWidth("10%")
                .setFooter(addRowbutton);
        
            Button deleteCatenaButton = new Button("Remove", VaadinIcon.MINUS.create());
            deleteCatenaButton.setWidthFull();//TODO
            deleteCatenaButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
            deleteCatenaButton.addClickListener(e -> {                            
                //TODO: unecessary at this time
            });  
            
        Grid.Column<CustomObject> deleteColumn = grid.addColumn(
                new ComponentRenderer<>(Button::new, (button, item) -> {
                    //Button button = new Button();
                    button.addThemeVariants(ButtonVariant.LUMO_ICON,
                            ButtonVariant.LUMO_ERROR,
                            ButtonVariant.LUMO_TERTIARY);
                    button.setIcon(new Icon(VaadinIcon.TRASH));
                    button.addClassName("buttonFile");             
                    button.getElement().setProperty("title", "Delete");
                    button.addClickListener(e -> {
                                items.remove(item);
                                grid.getDataProvider().refreshAll();                        
                            });     
                }))
                .setKey("deleteColumn")
                .setHeader("Delete", Alignment.CENTER))
                .setWidth("10%")
                .setFooter(deleteCatenaButton);
                           
        /*
         * 
         */             
        HeaderRow headerAll = grid.prependHeaderRow();
        headerAll.join(c1Column, c2Column, c3Column, c4Column,e ditColumn, deleteColumn)
                .setComponent("GRID: "+inputId, Alignment.START));
                
        /*
         * 
         */
        c1Column.setTextAlign(ColumnTextAlign.START);
        c2Column.setTextAlign(ColumnTextAlign.CENTER);
        c3Column.setTextAlign(ColumnTextAlign.CENTER);
        c4Column.setTextAlign(ColumnTextAlign.CENTER);
        editColumn.setTextAlign(ColumnTextAlign.CENTER);
        deleteColumn.setTextAlign(ColumnTextAlign.CENTER);
        
    }
    
    
    private void setupEditor() {
        
        Binder<CustomObject> binder = new Binder<>(CustomObject.class);
        editor.setBinder(binder);
        editor.setBuffered(true);
        
        editor.addCloseListener(listener -> {                            
            items.removeIf(i -> i.getCateneControllatePK().getCodConsob()<=0);
            if(editor.getBinder().isValid())
                items.remove(listener.getItem());               

            grid.getDataProvider().refreshAll();
        });
        
        
        /*
         * 
         */
        TextField c1Field = new TextField();
        c1Field.setWidthFull();
        binder.forField(c1Field)
                .asRequired("do it!")
                .bind(CustomObject::getC1, CustomObject::setC1);
        grid.getColumnByKey("c1Column").setEditorComponent(c1Field);
        
        
        /*
         *
         */
        IntegerField c2Field = new IntegerField();
        c2Field.setWidthFull();
        binder.forField(c2Field)
                .withConverter(new Converter<Integer, BigDecimal>() {
                    @Override
                    public Result<BigDecimal> convertToModel(Integer prsntn, ValueContext vc) {
                        if(prsntn!=null && prsntn>0)
                            return Result.ok(new BigDecimal(prsntn));
                        else 
                            return Result.ok(BigDecimal.ZERO);
                    }
                    @Override
                    public Integer convertToPresentation(BigDecimal model, ValueContext vc) {
                        if(model!=null && model.intValue()>0)
                            return model.intValue();
                        else
                            return 0;
                    }
                })
                .bind(CustomObject::getC2, CustomObject::setC2);
        grid.getColumnByKey("c2Column").setEditorComponent(c2Field);
        
                    
        IntegerField c3Field = new IntegerField();
        c3Field.setWidthFull();
        binder.forField(c3Field)
                .withConverter(new Converter<Integer, BigDecimal>() {
                    @Override
                    public Result<BigDecimal> convertToModel(Integer prsntn, ValueContext vc) {
                        if(prsntn!=null && prsntn>0)
                            return Result.ok(new BigDecimal(prsntn));
                        else 
                            return Result.ok(BigDecimal.ZERO);
                    }
                    @Override
                    public Integer convertToPresentation(BigDecimal model, ValueContext vc) {
                        if(model!=null && model.intValue()>0)
                            return model.intValue();
                        else
                            return 0;
                    }
                })
                .bind(CustomObject::getC3, CustomObject::setC3);
        grid.getColumnByKey("c3Column").setEditorComponent(c3Field);
        
        IntegerField c4Field = new IntegerField();
        c4Field.setWidthFull();
        binder.forField(c4Field)
                .withConverter(new Converter<Integer, BigDecimal>() {
                    @Override
                    public Result<BigDecimal> convertToModel(Integer prsntn, ValueContext vc) {
                        if(prsntn!=null && prsntn>0)
                            return Result.ok(new BigDecimal(prsntn));
                        else 
                            return Result.ok(BigDecimal.ZERO);
                    }
                    @Override
                    public Integer convertToPresentation(BigDecimal model, ValueContext vc) {
                        if(model!=null && model.intValue()>0)
                            return model.intValue();
                        else
                            return 0;
                    }
                })
                .bind(CustomObject::getC4, CustomObject::setC4);
        grid.getColumnByKey("c4Column").setEditorComponent(c4Field);

            
        /*
         * BUTTON - SAVE ROW
         */
        Button saveRowButton = new Button(VaadinIcon.CHECK.create(), e -> {
                                                if(binder.isValid()){
                                                    editor.save();                                                    
													grid.getDataProvider().refreshAll();
                                                }else
                                                    binder.validate();
                                       });
        saveRowButton.getElement().setProperty("title", "Save");
        saveRowButton.addClassName("buttonFile");
        
        /*
         * BUTTON - CANCEL CURRENT OPERATION
         */
        Button cancelButton = new Button(VaadinIcon.CLOSE.create(),
                e -> {                                               
                            if(!editor.getBinder().isValid())
                                items.remove(editor.getItem());
                            
                            editor.cancel();
                            grid.getDataProvider().refreshAll();  
                     });
        cancelButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
        cancelButton.getElement().setProperty("title", "Cancel");

        //
        HorizontalLayout actions = new HorizontalLayout(saveRowButton, cancelButton);
        actions.setPadding(true);
        actions.setAlignItems(Alignment.CENTER);
        actions.setJustifyContentMode(JustifyContentMode.CENTER);
        grid.getColumnByKey("editColumn").setEditorComponent(actions); 
        
        
        
    }
}

Good that you got everything working!

I became curious about one detail so I created the simplest possible example based on having multiple in-memory grids with removable items.

@Route
public class RemoveFromGridView extends VerticalLayout {
    public RemoveFromGridView() {
        add(new Button("Create grid", click -> add(createGrid())));
    }

    private Grid<String> createGrid() {
        Grid<String> grid = new Grid<>();
        
        List<String> items = new ArrayList<>(List.of("One", "Two", "Three"));
        grid.setItems(items);
        
        grid.addColumn(item -> item).setHeader("Value");
        grid.addComponentColumn(item -> new Button("Remove", click -> {
            items.remove(item);
            grid.getDataProvider().refreshAll();
        }));
        return grid;
    }
}

One thing to note in your example is related to using a hash map for the items. This might lead to confusion since the iteration order of a hash set doesn’t seem to follow any logic. You might want to use an ArrayList or LinkedHashSet instead since they preserve ordering so that the most recently added item always comes last in the list.