Как перевернуть сетку ваадина?

Я пытаюсь создать простое Java-приложение, используя Spring Boot и Vaadin. Мне нужно добавить таблицу в пользовательский интерфейс следующим образом: https://www.screencast.com/t/1c4xkr4IE

Он может быть продлен периодами.

Похоже, что элемент Vaadin Grid идеально соответствует моим требованиям, но он добавляет мои строки в виде столбцов. Можно ли перевернуть сетку или, может быть, есть другой способ построить нужную таблицу?

ОБНОВИТЬ

Вот мой код:

@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());
    });
}
}

Итак, что я имею в виду под этим вопросом, так это то, что я установил

grid.setColumns("month", "numberOfWorkers", "numberOfNewcomers", "numberOfDismissals");

и не узнать метод, подобный setRows, поэтому моя таблица выглядит так: https://www.screencast.com/t/ndDY6tXp, но должно быть как на первой картинке.


person khris    schedule 28.03.2017    source источник
comment
Вы должны иметь возможность добавлять любые столбцы, которые вы хотите. Однако в некоторых случаях (например, если столбец содержит несколько типов данных) это может быть сложно. Вы должны опубликовать свой код, обязательно, иначе проблема не ясна.   -  person default locale    schedule 28.03.2017
comment
я обновил вопрос   -  person khris    schedule 29.03.2017


Ответы (2)


Я считаю, что невозможно элегантно решить эту проблему без использования CSS или расширения компонента клиентской сетки.

Что вы можете сделать, так это добавить свои данные, используя

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)
person Manuel Kollegger    schedule 30.03.2017

Я считаю, что компонент Vaadin сетка (или таблица) был разработан с использованием концепции таблиц в качестве отправной точки. Следовательно, у вас будет единая структура, определяемая столбцами, и будет отображаться любое количество элементов данных одного типа, по 1 в строке. Насколько я знаю, до версии 8.0.4 нельзя повернуть структуру.

Кроме того, с точки зрения взаимодействия с пользователем, если у вас есть несколько периодов времени, будет проще прокручивать их вертикально (с помощью колесика мыши), чем горизонтально, поэтому я d предложить обсудить возможность отображения их так же, как вы начали, со столбцами «месяц», «количество рабочих», «количество новых прибывших» и «количество уволенных» и предоставить строки MyData. Это также упрощает сортировку, фильтрацию, добавление или редактирование выбранных элементов, тогда как для обходного пути ниже вам придется сделать что-то дополнительное.

Если по какой-то причине это вообще неприемлемо, вы сможете подделать нужную функцию, немного поработав (см. пример ниже), но с точки зрения производительности и удобства использования нет никаких гарантий... в конце концов, это не то, что это было разработано для.

Код

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's 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);
        }
    }
}

Результат

Горизонтальная сетка

person Morfic    schedule 30.03.2017