【问题标题】:Unkown Key in Vaadin 14 Grid during selection选择期间 Vaadin 14 Grid 中的未知键
【发布时间】:2020-09-13 07:54:26
【问题描述】:

我在 Vaadin 14 中使用Grid。网格处于多选模式。 选择处理程序需要几秒钟才能完成,我在最后调用 setItems(...) 来更新网格中的项目。

当用户在前一个选择处理程序仍在运行时选择另一行时,我收到类似于https://github.com/vaadin/vaadin-grid-flow/issues/322 中描述的“未知键”错误,即使新的项目集仍然包含选定的项目(另一个对象实例,但根据equals() 相同)。这似乎是因为KeyMapper 中的密钥已因setItems() 而更改,因此来自客户端的密钥不再存在。

有没有办法解决这个问题,例如在上一个请求正在进行时禁用选择?

更新

为了解决this Vaadin bug,我还调用setPageSize(),并使用确切的项目数作为参数。不过好像我不打电话setPageSize()也会出现同样的问题,所以很可能是setItems()造成的。

【问题讨论】:

  • 问题可能来自您拨打setItems(..)。也可能来自 pageSize 的东西,idk。我个人会重新考虑重新设置项目是否是在 selectionListener 中做的正确事情。理论上,您可以通过首先在 selectionListener 中生成一个后台线程来解决您的问题,并通过使用 ui.access(..) 禁用整个网格来禁用选择。然后将执行此操作,而无需等待整个侦听器完成。
  • @kscherrer 选择一个项目时,它必须在 DB 中更新并反映在网格中,对于某些项目,当取消选择它们时,它们必须从 DB 和网格中删除。这是所需的功能。所以在数据库操作之后我刷新了网格。有没有更合适的方法来实现这种行为?感谢您使用后台线程的建议,但我希望有一种更清洁的方法。我现在很确定它来自 pageSize,重置会导致键被刷新。
  • @kscherrer 好的,即使没有调用setPageSize,键似乎也已刷新。所以这是由于setItems(...)
  • 你可以等到用户点击一个按钮,例如“保存选定的项目”。这听起来也有点像你可以为你想要的功能做你自己的专栏。添加一个带有 Checkbox 的列,然后您有一个 valueChangeListener 可以将内容保存到 DB 中。
  • 您是否使用选择状态来定义项目的布尔字段?

标签: vaadin vaadin-flow vaadin-grid vaadin10


【解决方案1】:

不要不要更改 SelectionListener 中的网格项目。 你仍然可以做你想做的所有事情,但实际上并不需要重新设置项目。事实上,它只会造成您现在遇到的问题。

在处理这个答案时,我意识到您需要自己做复选框列,以便能够对刚刚“选择”的一项执行操作,而不是删除所有项然后添加所有选定项(因为更好的性能)。这就是它的样子。

// in my code samples, a `Foo` item can have many `Bar` items. The grid is of type Bar.

Grid.Column customSelectionColumn = grid.addComponentColumn(item -> {
    Checkbox isSelected = new Checkbox();
    isSelected.setValue(someParentFoo.getBars().contains(item));
    isSelected.addValueChangeListener(event -> {
        boolean newSelectedValue = event.getValue();
        if(newSelectedValue){
            someParentFoo.getBars().add(item)
        } else {
            someParentFoo.getBars().remove(item);
        } 
        fooRepository.save(someParentFoo);
    });
});
// make a Checkbox that selects all in the header
Checkbox toggleSelectAll = new Checkbox();
toggleSelectAll.addValueChangeListener(event -> {
    if(event.getValue()){
        someParentFoo.getBars().addAll(allGridItems);
    } else {
        someParentFoo.getBars().removeAll(allGridItems);
    }
    fooRepository.save(someParentFoo);
    grid.getDataProvider().refreshAll(); // updates custom checkbox value of each item
});
gridHeaderRow.getCell(customSelectionColumn).setComponent(toggleSelectAll);

【讨论】:

  • 除非我遗漏了什么,否则这不允许我在取消选择时删除某些行(正如我在问题的评论线程中提到的那样)对吧?对于这种情况,我无论如何都可以调用setItems,但在其中放置一个模式删除确认弹出窗口,以避免用户取消选择某些内容,然后在前一个事件处理程序完成之前选择其他内容。
  • 是的。它不会从网格中删除行,但会删除 foo 和取消选择的 bar 之间的关系并将新状态保存在 DB 中。
  • 不,不要调用 setItems!
  • 我删除了滥用 Vaadins Grid 的行选择功能的代码。我认为您应该逐步重新评估用户体验。最重要的是,selectionListener 没有用于获取刚刚切换选择的项目的 API,这使得这一切变得复杂 3 倍,因为用户一次只能选择/取消选择一项。
  • 我已经有了计算新选择和取消选择项目的代码,它只有几行。但是要求实际上是在取消选择时从网格中删除某些行。这些项目仅存在于我要从中删除它们的集合中。其他项目存在于一个中,选择意味着相应的项目存在于另一个中。对于后者没有问题,但前者需要从网格中删除。如果用户转到另一个页面并返回,他们将消失(因为数据已被删除)所以如果他们一开始留在网格中会很奇怪。
【解决方案2】:

我解决了这个问题。 Vaadin 使用数据作为 HashMap 中的键。您需要 calc hashCode 使用不可变数据字段。例如

public class TestData {

    private int id;
    private String name;

    public TestData(int id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

【讨论】:

  • 我没有改变任何字段。使用新对象实例调用 setItems。
猜你喜欢
  • 2022-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
相关资源
最近更新 更多