【问题标题】:How do I pre-select items in a Vaadin 21 Grid? Selection is not displayed in the UI如何在 Vaadin 21 网格中预先选择项目?选择不显示在 UI 中
【发布时间】:2021-12-30 22:44:57
【问题描述】:

我已经定义了一个实现BeforeEnterObserver 的布局。在beforeEnter 方法中,我从数据库加载一个对象实例。该实例与其他对象有关系。 同样在这个方法中,我调用了另一个 Layout 的方法,它设置 bean 并尝试在 Grid 中预选项目:

    public void setBean(BeanClass bean) {
        binder.setBean(bean);
        relationGrid.deselectAll();
        for(Person person : bean.getRelated()) {
            relationGrid.getGrid().select(person);
        }
        Set<Person> selected = involvedGrid.getGrid().getSelectedItems();
    }

最后一行用于调试。我可以看到,已经加载的 bean 正在正确选择相关实例。

Grid 是在布局内它自己的类中定义的,具有多选且没有全选复选框和搜索字段:

grid.setSelectionMode(Grid.SelectionMode.MULTI);
GridMultiSelectionModel<Person> selectionModel = (GridMultiSelectionModel<Person>) grid.getSelectionModel();
selectionModel.setSelectAllCheckboxVisibility(GridMultiSelectionModel.SelectAllCheckboxVisibility.HIDDEN);

Grid布局被注入到表单视图中,表单视图被注入到编辑视图中。

现在在浏览器中显示编辑视图时,不会检查 Grid 中的相关对象。 我在编辑视图中配置了一个按钮,以显示所选项目,用于调试目的:

        cancel.addClickListener(e -> {
                Set<Person> selectedPersons = form.getRelatedGrid().getSelectedItems();
                for(Person person : selectedPersons)
                    Notification.show(person.getDisplayName());
        });

如果我点击按钮,它会正确显示相关人员的姓名。如果我然后选择另一个人并再次单击,它会正确地将两个选定的人显示为通知,但在网格中只选择了第二个。

表单的字段已正确填写,如上所示,我之后直接处理关系。

我也尝试启用推送模式

@Push
public class Application extends SpringBootServletInitializer implements AppShellConfigurator {

    public static void main(String[] args) {
                SpringApplication.run(Application.class, args);
    }
}

这不会改变网格的行为。

为简化起见,在与大图讨论后,我做了一个测试视图。同样的问题仍然存在。

@Route(value = "event/:id([0-9]+)/edit_test", layout = MainLayout.class)
public class EditEventTest extends VerticalLayout implements BeforeEnterObserver, AfterNavigationObserver {

    private final EventRepository eventRepository;
    private final PersonRepository personRepository;

    private Long id;
    private Event event;

    private final TextField title = new TextField("Title");
    private final Grid<Person> persons = new Grid<>(Person.class, false);

    private final Button save = new Button("Save");
    private final Button test = new Button("Test");

    private Binder<Event> binder = new Binder<>(Event.class);

    @Autowired
    public EditEventTest(EventRepository eventRepository, PersonRepository personRepository) {
        this.eventRepository = eventRepository;
        this.personRepository = personRepository;
    }

    @Override
    public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
        id = Long.valueOf(beforeEnterEvent.getRouteParameters().get("id").orElseThrow(NotFoundException::new));
        Optional<Event> result = eventRepository.findById(id);
        this.event = result.orElseThrow(() -> new NoSuchRecordException("Could not find Event with id " + id));
    }

    @Override
    public void afterNavigation(AfterNavigationEvent event) {
        buildView();
        updateView();
    }

    private void buildView() {

        persons.setSelectionMode(Grid.SelectionMode.MULTI);
        GridMultiSelectionModel<Person> selectionModel = (GridMultiSelectionModel<Person>) persons.getSelectionModel();
        selectionModel.setSelectAllCheckboxVisibility(GridMultiSelectionModel.SelectAllCheckboxVisibility.HIDDEN);
        persons.addColumn(Person::getDisplayName).setHeader("Name");
        GridListDataView<Person> dataView = persons.setItems(personRepository.findAll());

        FormLayout form = new FormLayout();
        add(form);
        form.add(title);
        form.add(persons);

        binder.setBean(event);
        binder.bindInstanceFields(this);

        HorizontalLayout buttonLayout = new HorizontalLayout();
        buttonLayout.addClassName("button-layout");
        save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        buttonLayout.add(save);
        buttonLayout.add(test);
        add(buttonLayout);

        test.addClickListener(e-> {
            for (Person p : persons.getSelectedItems())
                Notification.show(p.getDisplayName());
        });

    }

    private void updateView() {
        persons.deselectAll();
        for (Person person : event.getInvolved())
            persons.select(person);
    }

}

我还尝试将buildView()移动到构造函数,将binder.setBean(event)移动到updateView()。问题依然存在。

我在这里遗漏了什么或者这是一个错误?

【问题讨论】:

  • 我会说使用 AfterNavigationObserver 在语义上更正确的生命周期事件来执行此操作。
  • @TatuLund 但是在 BeforeEnterObserver 中从数据库中获取对象更有意义,因为我可以使用 beforeEnterEvent.getRouteParameters()。你的意思是初始化网格吗?
  • BeforeEnterObserver 更多用于访问控制等。您应该通过实现HasUrlParameter来处理url参数,解析并存储到一个类字段,然后在afterNavigation中使用。
  • @TatuLund 这不会改变网格中选择的行为,但它会破坏我的 URL 架构,架构应该是“bean/:personId/edit”,现在我得到一个 URL 匹配器,例如这个:“bean/:personId([0-9]+)/edit/:___url_parameter(^[+-]?[0-8]?[0-9]{1,18}$)”。所以如果我把ID放在后面,Grid选择问题依然存在。
  • 你自己的文档是这样描述的:vaadin.com/docs/v21/flow/routing/templates/…

标签: java vaadin vaadin-grid vaadin21


【解决方案1】:

您的代码对我来说看起来不错。我只是建议将数据的获取从 beforeEnter 移到 afterNavigation,但它不应该影响结果。这是您应该在更广泛的范围内考虑的事情,即在肯定需要之前不要从后端加载数据。例如,您将来可能在其他视图中或在此视图中在 beforeEnter 中进行访问控制。将鼠标悬停在这与您的实际问题无关。

这个方法大概可以简化

private void updateView() {
    persons.deselectAll();
    for (Person person : event.getInvolved())
        persons.select(person);
}

private void updateView() {
    persons.asMultiSelect().setValue(event.getInvolved());
}

但我也不希望它改变行为。如果确实如此,则 Grid#select 中存在错误,因为在多选模式下,它不应重置以前的选择。

【讨论】:

  • 我可以为此在 gitlab 上提交问题吗?我还发现了 Grid 的其他问题,过滤时很难解释(不过我可以制作视频)。
  • Vaadin 组件的问题跟踪器是 github.com/vaadin/flow-components/issues 您需要包含重现问题的最少代码示例/应用程序。
猜你喜欢
  • 1970-01-01
  • 2022-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 2019-07-16
  • 1970-01-01
相关资源
最近更新 更多