How to cancel the vaadin grating?
I am trying to create a simple java application using Spring Boot and Vaadin. I need to add a table to the UI like this: https://www.screencast.com/t/1c4xkr4IE
It can be extended by periods.
It looks like the Vaadin Grid item suits my requirements perfectly, but it adds my rows as columns. Is it possible to reverse the grid, or maybe there is another way to create the required table?
UPDATE
Here is my code:
@SpringComponent
@UIScope
public class MyDataEditor extends VerticalLayout {
private final MyDataRepository repository;
private MyData myData;
TextField month = new TextField("Period");
TextField numberOfWorkers = new TextField(" Number of workers");
TextField numberOfNewcomers = new TextField("Number of newcomers");
TextField numberOfDismissals = new TextField("Number of dismissals");
Button save = new Button("Save");
Button cancel = new Button("Cancel");
Button delete = new Button("Delete");
CssLayout actions = new CssLayout(save, cancel, delete);
Binder<MyData> binder = new Binder<>(MyData.class);
@Autowired
public MyDataEditor(MyDataRepository repository) {
this.repository = repository;
addComponents(month, numberOfWorkers, numberOfNewcomers, numberOfDismissals, actions);
binder.bindInstanceFields(this);
setSpacing(true);
actions.setStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
save.setStyleName(ValoTheme.BUTTON_PRIMARY);
save.setClickShortcut(ShortcutAction.KeyCode.ENTER);
save.addClickListener(e -> repository.save(myData));
delete.addClickListener(e -> repository.delete(myData));
cancel.addClickListener(e -> editInputData(myData));
setVisible(false);
}
public interface ChangeHandler {
void onChange();
}
public final void editMyData(MyData c) {
if (c == null) {
setVisible(false);
return;
}
final boolean persisted = c.getMonth() != null;
if (persisted) {
myData = repository.findOne(c.getMonth());
} else {
myData = c;
}
cancel.setVisible(persisted);
binder.setBean(myData);
save.focus();
periodId.selectAll();
}
public void setChangeHandler(ChangeHandler h) {
save.addClickListener(e -> h.onChange());
delete.addClickListener(e -> h.onChange());
}
}
@SpringUI
@Theme("valo")
public class VaadinUI extends UI {
private final MyDataRepository repo;
private final MyDataEditor editor;
final Grid<MyData> grid;
private final Button addNewBtn;
@Autowired
public VaadinUI(MyDataRepository repo, MyDataEditor editor) {
this.repo = repo;
this.editor = editor;
this.grid = new Grid<>(MyData.class);
this.addNewBtn = new Button("Add new month");
}
@Override
protected void init(VaadinRequest request) {
grid.setHeight(300, Unit.PIXELS);
grid.setColumns("month", "numberOfWorkers", "numberOfNewcomers", "numberOfDismissals");
grid.asSingleSelect().addValueChangeListener(e -> {
editor.editMyData(e.getValue());
});
addNewBtn.addClickListener(e -> editor.editMyData(new MyData()));
editor.setChangeHandler(() -> {
editor.setVisible(false);
grid.setItems(repo.findAll());
});
}
}
So what I mean by this question is what I installed
grid.setColumns("month", "numberOfWorkers", "numberOfNewcomers", "numberOfDismissals");
and don't recognize the type method setRows
, so my table looks like this: https://www.screencast.com/t/ndDY6tXp but should be like the first picture.
source to share
I truly believe that there is no way to solve this gracefully without CSS or extending the client's grid component.
What you could do is add your data using
List<MyData> data = repo.findAll();
for(int i = 0; i < data.size(); i++)
grid.addColumn(i)
//String[] months = data.map(x -> x.month).collect(Collectors.toArray)
//String[] nrWork = data.map(x -> x.nrWork).collect(Collectors.toArray)
grid.addRow(months)
grid.addRow(nrWork)
source to share
I believe the Vaadin grid (or table) component was designed with the concept of a table as a starting point. Therefore, you will have a single column-defined structure that displays any number of data items of the same type, 1 per row. And as far as I know, prior to 8.0.4, you cannot rotate the structure.
Also, from a user perspective, if you have multiple time periods, it will be easier to scroll vertically (using the mouse wheel) than horizontally, so I suggest discussing the possibility of displaying them the same way you started with the "month "," numberOfWorkers "," numberOfNewcomers "and" numberOfDismissals "and supplied the strings MyData
. It also makes it easier to sort, filter, add or edit the selected items, whereas for the workaround below you need to do something extra.
If this is not acceptable for some reason, you should be able to spoof the function you want with a little work (see sample below), but performance and usability are wise, there are no guarantees ... after all, this is not what it is for.
code
package com.example.grid;
import com.vaadin.data.ValueProvider;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Notification;
import com.vaadin.ui.VerticalLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
public class HorizontalGrid extends VerticalLayout {
private static final String ROW_CAPTION = "row-caption";
public HorizontalGrid() {
// basic grid setup without column header
Grid<HorizontalDisplayAdapter> grid = new Grid<>();
grid.setSizeFull();
grid.setSelectionMode(Grid.SelectionMode.NONE);
grid.removeHeaderRow(0);
// load some data from the DB or someplace else
List<PeriodSummary> periods = loadPeriods();
// add row headers
grid.addColumn(HorizontalDisplayAdapter::getCaption).setId(ROW_CAPTION).setWidth(150);
// add a column for each period
for (int i = 0; i < periods.size(); i++) {
// save the column index so we ca figure out what to edit later
grid.addColumn(new AdapterValueProvider(i)).setId(String.valueOf(i)).setWidth(60);
}
// wrap the data in "horizontal display adapters"
grid.setItems(
new HorizontalDisplayAdapter("Period", periods, PeriodSummary::getPeriod),
new HorizontalDisplayAdapter("Workers", periods, PeriodSummary::getWorkers),
new HorizontalDisplayAdapter("Newcomers", periods, PeriodSummary::getNewcomers),
new HorizontalDisplayAdapter("Dismissals", periods, PeriodSummary::getDismissals)
);
// retrieve the correct period summary to edit, based on the column that was clicked (unless it the header)
grid.addItemClickListener(event -> {
if (!ROW_CAPTION.equals(event.getColumn().getId())) {
Integer columnIndex = Integer.valueOf(event.getColumn().getId());
Notification.show("Editing " + event.getItem().getSummary(columnIndex), Notification.Type.ERROR_MESSAGE);
}
});
// freeze first column for scrolling purposes
grid.setFrozenColumnCount(1);
addComponent(grid);
setSizeFull();
}
// generate some dummy data to simulate loading from the DB
private List<PeriodSummary> loadPeriods() {
Random random = new Random();
ArrayList<PeriodSummary> periodSummaries = new ArrayList<>();
for (int i = 0; i < 100; i++) {
periodSummaries.add(new PeriodSummary(i, random.nextInt(100), random.nextInt(100), random.nextInt(100)));
}
return periodSummaries;
}
// adapter to display data in a "horizontal format"
public class HorizontalDisplayAdapter {
// row caption
private final String caption;
// periods for each column
private final List<PeriodSummary> periods;
// used for brevity, a class hierarchy is probably more elegant
private Function<PeriodSummary, Integer> valueExtractor;
public HorizontalDisplayAdapter(String caption, List<PeriodSummary> periods, Function<PeriodSummary, Integer> valueExtractor) {
this.caption = caption;
this.periods = periods;
this.valueExtractor = valueExtractor;
}
public String getCaption() {
return caption;
}
public PeriodSummary getSummary(int columnIndex) {
return periods.get(columnIndex);
}
// extract the data for a certain column
public Integer getValue(int columnIndex) {
return valueExtractor.apply(periods.get(columnIndex));
}
}
// basic bean
public class PeriodSummary {
int period;
int workers;
int newcomers;
int dismissals;
public PeriodSummary(int period, int workers, int newcomers, int dismissals) {
this.period = period;
this.workers = workers;
this.newcomers = newcomers;
this.dismissals = dismissals;
}
public int getPeriod() {
return period;
}
public void setPeriod(int period) {
this.period = period;
}
public int getWorkers() {
return workers;
}
public void setWorkers(int workers) {
this.workers = workers;
}
public int getNewcomers() {
return newcomers;
}
public void setNewcomers(int newcomers) {
this.newcomers = newcomers;
}
public int getDismissals() {
return dismissals;
}
public void setDismissals(int dismissals) {
this.dismissals = dismissals;
}
@Override
public String toString() {
return "PeriodSummary{" +
"period=" + period +
", workers=" + workers +
", newcomers=" + newcomers +
", dismissals=" + dismissals +
'}';
}
}
// value provider for the horizontal display adapters
private class AdapterValueProvider implements ValueProvider<HorizontalDisplayAdapter, Integer> {
// column index is used to retrieve data from the correct summary
private int columnIndex;
public AdapterValueProvider(int columnIndex) {
this.columnIndex = columnIndex;
}
@Override
public Integer apply(HorizontalDisplayAdapter horizontalDisplayAdapter) {
return horizontalDisplayAdapter.getValue(columnIndex);
}
}
}
Result
source to share