Search code examples
javaspringvaadinvaadin-flowvaadin-grid

Vaadin Grid - data from multiple objects


I would like to show data from two different objects in one Grid. I have "folders" and "items" and I would like to see them together in one Grid. Folders at the top and items below folders. Something like list view in file manager application. But I don't know how to get the data together. I could probably create an abstract parent class for Item and Folder classes with getter methods, which I'm using in the grid. Is there any better solution for that?

Expected result:

expected result

@Route(value = "")
@PageTitle("Items | Test")
public class ListView extends VerticalLayout {
    
    Grid<Item> grid = new Grid<>(Item.class);
    ItemService service;

    Folder currentFolder;

    public ListView(ItemService service) {
        this.service = service;
        this.currentFolder = service.getAllFolders().get(0);
        addClassName("list-view");

        add(getGrid());
        updateList();
    }

    private HorizontalLayout getGrid() {
        HorizontalLayout layout = new HorizontalLayout(grid);
        layout.setSizeFull();

        grid.addClassNames("grid");
        grid.setColumns("name");
        grid.addColumn(testCase -> testCase.getStatus().getValue()).setHeader("Status").setSortable(true);
        grid.addColumn(new ComponentRenderer<>(
                        testCase -> {
                            Checkbox checkbox = new Checkbox();
                            checkbox.setValue(testCase.getBooleanValue());
                            checkbox.setReadOnly(true);
                            return checkbox;
                        }
                )
        ).setHeader("Boolean");
        grid.getColumns().forEach(col -> col.setAutoWidth(true));

        return layout;
    }

    private void updateList() {
        grid.setItems(service.getItemsFromFolder(currentFolder));
    }
}

Service:

@Service
public class ItemService {

    private final ItemRepository itemRepository;
    private final FolderRepository folderRepository;

    public ItemService(ItemRepository itemRepository, FolderRepository folderRepository) {
        this.itemRepository = itemRepository;
        this.folderRepository = folderRepository;
    }

    public List<Folder> getAllFolders() {
        return folderRepository.findAll();
    }

    public List<Item> getItemsFromFolder(Folder folder) {
        return itemRepository.getItemsFromFolder(folder.getId());
    }

}

Item Repository:

public interface ItemRepository extends JpaRepository<Item, Long> {

    @Query("Select i from Item i where i.folder.id = :folderId")
    List<Item> getItemsFromFolder(@Param("folderId") Long folderId);
}

Folder Repository:

public interface FolderRepository extends JpaRepository<Folder, Long> {
    
}

Item Entity:

@Entity
@Getter
@Setter
public class Item {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotEmpty
    private String name = "";

    @Enumerated
    @Column(columnDefinition = "smallint")
    private Status status;

    @NotNull
    private Boolean booleanValue;

    @ManyToOne
    @JoinColumn(name = "folder_id")
    @NotNull
    private Folder folder;

}

Folder Entity:

@Entity
@Getter
@Setter
public class Folder {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Long parentFolderId;

}

Status enum:

@AllArgsConstructor
public enum Status {

    DRAFT("Draft"),
    READY("Ready"),
    OBSOLETE("Obsolete");

    @Getter
    private final String value;

}

Solution

  • Grid can only have one type of bean, so an abstract parent class or interface is the way to go. But given the description of your use case of "folders" and "items", have you considered using TreeGrid instead?