Vaadin 21 流。如何迁移用于具有带边框的面板的 CustomLayout

Vaadin 21 flow. How to migrate CustomLayout used to have a panel with border

使用 vaadin 7(我们正在尝试迁移到 v21,非常非常困难)我们有这个

CustomLayout cl1 = new CustomLayout(new ByteArrayInputStream("<fieldset><legend location='legend'></legend><div location='content'></div></fieldset>".getBytes()));
cl1.setSizeUndefined();
cl1.add(new Label(title), "legend");
cl1.add( panel, "content");

基本上是一个带边框的面板 title-border 我们如何在 vaadin flow v21

中做到这一点

提前致谢

Vaadin 10+ 为最常用的 HTML 标签定义了“元素”,并对构建在这些元素之上的组件进行了更高级别的抽象。它不包括 <fieldset> 的元素或组件。我对 Vaadin 7 不熟悉,但它似乎也没有附带。

有多种方法可以使用 Vaadin 10+ 执行您想要的操作。这是一个基于扩展 Component class:

的快速示例
@Tag("fieldset")
public class FieldSet extends Component {

    private final Div enclosedComponents;

    public FieldSet(String label) {
        Element legend = new Element("legend").setText(label);
        getElement().appendChild(legend);
        enclosedComponents = new Div();
        getElement().appendChild(enclosedComponents.getElement());
    }

    public void add(Component ... components) {
        enclosedComponents.add(components);
    }
}

我没有包含稳健的 API。完全补充添加和删除方法以及更新标签的方法会更有用。

作为学习 10+ 的一个要点,要知道 fieldset 的性质使这个更加复杂。如果这不必包含 <legend> 标记,它可能会简单得多,因为您可以简单地扩展 Div 或几个 Layout classes 之一并继承一个健壮的API.

有一个 section of the documentation 概述了解决这些类型问题的各种方法。当我第一次开始使用 Vaadin 时,我发现它非常宝贵。何时使用每种方法并不总是很清楚,但您会体会到的。

有一个 Cookbook 食谱为 CustomLayout 提供了替代方案:https://cookbook.vaadin.com/custom-layout

本质上,CustomLayout 替换 class 以相当直接的方式扩展了 Htmladd 方法具有大部分逻辑:

public class CustomLayout extends Html {
    private Map<String, Component> locations = new HashMap<>();

    public CustomLayout(String template) {
        super(template);
    }

    public CustomLayout(InputStream stream) {
        super(stream);
    }

    public void add(Component child, String location) {
        remove(location);
        locations.put(location, child);

        // Establish parent-child relationship, but leave DOM attaching to us
        getElement().appendVirtualChild(child.getElement());

        // Attach to the specified location in the actual DOM
        getElement().executeJs("this.querySelector('[location=\"'+[=10=]+'\"]').appendChild()", location,
                child.getElement());

        // Ensure the element is removed from the DOM when it's detached
        child.addDetachListener(detachEvent -> {
            detachEvent.unregisterListener();
            getElement().executeJs("this.querySelector && this.querySelector('[location=\"'+[=10=]+'\"]').lastChild.remove()", location);

            // Also clear the bookkeeping
            locations.remove(location, child);
        });
    }

    public void remove(String location) {
        Component oldChild = locations.remove(location);
        if (oldChild != null) {
            remove(oldChild);
        }
    }

    public void remove(Component child) {
        getElement().removeVirtualChild(child.getElement());
    }
}

请注意,使用 locations 映射进行簿记很重要,这样在父元素分离后客户端元素也会被删除。