My intention: I'd like to use a HTML-/JavaScript component in a Vaadin (23.1.4) application. This component is hidden initially (because its parent wrapper component is hidden). The component should get (JavaScript-)updates from server-side.
Problem: As this component initially is hidden, it is not part of the DOM yet. And therefore it is not available to get updates with JavaScript commands from server-side.
Question: Is there an event for a component when it gets visible / un-hidden (because a parent components gets visible / un-hidden)?
What I tried / what does not seem to be a solution:
- the method
setVisible(boolean)
in MyCustomField is not called (because it is wrapped in a parent component). So I can't listen to that. - an AttachListener of MyCustomField is called when the UI loads. It's not called when MyCustomField is getting un-hidden.
- method
setEnabled(boolean)
in MyCustomField is not called. So I can't listen to that.
Possible workarounds:
- Show all components in the form and hide them later. This is not the intended approach for me because there are some components that need some time to load. All of them loaded initially would need a lot of time.
- Execute the JavaScript update commands within an interval that checks the visibility of MyCustomField and executes all collected commands when MyCustomField is visible.
Code to reproduce:
This is a very small custom field that updates some of its content by JavaScript:
private static class MyCustomField extends CustomField<String> {
public MyCustomField() {
this.add(new Html("<div id=\"myField\">Initial value</div>"));
}
protected void setPresentationValue(String newPresentationValue) {
UI.getCurrent().getPage().executeJs("document.getElementById('myField').innerHTML='"+newPresentationValue+"';");
}
protected String generateModelValue() {return null;}
}
And this is my View:
@Route("myview")
public class MyView extends VerticalLayout {
public MyView() {
MyCustomField myCustomField = new MyCustomField();
Div wrapperComponent = new Div();
wrapperComponent.setId("wrapper");
wrapperComponent.setVisible(false);
wrapperComponent.add(myCustomField);
Button toggleVisibilityButton = new Button("Toggle visibility");
toggleVisibilityButton.addClickListener(clickEvent -> {
wrapperComponent.setVisible(!wrapperComponent.isVisible());
});
Button setNewValueButton = new Button("Set new value");
setNewValueButton.addClickListener(clickEvent -> {
myCustomField.setPresentationValue(LocalDateTime.now().toString());
});
this.add(wrapperComponent, toggleVisibilityButton, setNewValueButton);
}
When the view loads, there is only this empty hidden wrapper element: <div hidden="true"></div>
and two buttons.
When the user clicks the "Set new value" button, the browser shows an error: document.getElementById(...) is null
(because the element 'myField' does not exist in the DOM yet). So here some JavaScript-actions get lost.
When the user first clicks the "Toggle visibility" button and then the "Set new value" button, then everything works fine.