1

with vaadin 7 ( we are trying to migrate to v21, very, very, hard) we have this

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");

Basically is a panel with border and title-border How we can do this in vaadin flow v21

Thanks in advance

Anna Koskinen
  • 1,362
  • 3
  • 22

2 Answers2

1

Vaadin 10+ defines "elements" for most commonly used HTML tags, and has higher level abstractions for components built on top of those elements. It does not include an element or a component for <fieldset>. I'm not familiar with Vaadin 7, but it looks like it didn't come with it either.

There are a couple of ways to do what you want with Vaadin 10+. Here's a quick example based on extending the 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);
    }
}

I did not include a robust API. It would be more useful with a full compliment of add and remove methods, as well as a means to update the label.

As a point of learning 10+, know that the nature of fieldset makes this one more complicated. If this did not have to include the <legend> tag it could be far simpler, because you could simply extend Div or one of the several Layout classes and inherit a robust API.

There is a section of the documentation that outlines the various ways to solve these types of problems. I found it invaluable when I first started using Vaadin. It's not always clear when to use each of the approaches, but you'll get the feel for it.

gruntled
  • 418
  • 2
  • 6
1

There's a Cookbook recipe that provides an alternative for CustomLayout: https://cookbook.vaadin.com/custom-layout

Essentially, the CustomLayout replacement class extends Html in a fairly straightforward way. The add method has most of the logic:

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=\"'+$0+'\"]').appendChild($1)", 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=\"'+$0+'\"]').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());
    }
}

Note that it's important to do the bookkeeping with the locations Map so that client-side elements get removed too after the parent is detached.

ollitietavainen
  • 3,900
  • 13
  • 30