If getIconPath returns an URL with valid content on the server, the image is displayed.
If it returns an URL with invalid (no content → 404) then the “broken image” symbol is hidden (that’s what I wanted to achieve). But then if I update the URL again with a valid URL, then the image does not appear.
By inspecting the HTML I see that the style “display: none;” is not removed from the element, despite the call to icon.setVisible(true);
In order for my code to work I’ve had to add :
icon.getElement().executeJs("this.style.display='flex'");```
This happens on both Vaadin 24.4.18 and Vaadin 24.5.6
Am I doing something wrong or is it a known issue ?
Thanks.
The onerror puts the style on the element, but there’s nothing that removes the style.
The same will happen if you do the same in plain html + js.
There’s probably many different ways to solve this, but the first one that comes to my mind is to use onerror to set a fallback image (e.g. a 1x1 px empty image), instead of setting a style, since that will be overridden when you supply a new src.
@marcoc_753 Please add something like this to the core:
If that was available, I think @Charles-Edouard would have never gotten into this trap and also the code (mixing totally different abstraction layers and languages) I have seen wouldn’t make me that depressed
The reason that setVisible(true) does not help is, that setVisible does not modify the style attribute, but simply adds/removes an hidden attribute to the client side element. But since you added an explicit style.display = 'none', the resulting client side style="display: none" will override any displays set by other css.
What you could do with already available features would be to fire a custom event from the client and listen to that in Flow, for instance
// ... in Java ...
Element element = anchor.getElement();
// register the client side on error listener and fire a custom event, that the anchor will listen to in Java
element.executeJs("this.addEventListener('onerror', e => this.dispatchEvent(new CustomEvent('image-error')))");
// the Java listener for the custom client side event
element.addEventListener("image-error", event -> /*... react on the error */);
This way you can handle for instance the visibility completely with your Java code without any additional manual client side modification.
This combination of a java script listener plus firing a custom event, that then will be listened on in Java can be used for many purposes and many components. It’s a very helpful tool to overcome missing built-in Java events
Hi Stefan,
thanks for the detailed explanation. I was not aware of the ability to easily add events like this.
This is a very good solution that could help solve other problems. I’ll keep that in mind !
Regards
Even though the code snippet by @Stefan.27 works, don’t use it as such. Just take it as a PoC. Reason 1: you are then easily mixing two different abstraction layers. Whatever the anchor class there is, that is the right place where Element API level things should be placed. Reason 2: the JS execution is not needed here at all. You can also listen to standard DOM events, not just CustomEvent’s.
Example:
// With raw Image component, but working with multiple abstraction levels in the same class
// which you should NEVER do. Just added here as an example Sorry for our API letting you do this....
Image rawImageThatFailsToo = new Image("https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.pngX", "Google logo");
rawImageThatFailsToo.getElement().addEventListener("error", event -> {
Notification.show("This fails too!");
// Hide in this case
rawImageThatFailsToo.setVisible(false);
});
add(rawImageThatFailsToo);
// It is also possible to "piggyback" on the DOM API if you want only one class and don't want to create
// another for the event. This is better, but not perfect, DomEventListeners and DomEvent are for component impls.
// Check the VImage class for a proper way to do this, MyImage below
MyImage myImage = new MyImage();
myImage.addErrorListener(event -> {
Notification.show("Yay, this works too!");
});
}
private static class MyImage extends Image {
public Registration addErrorListener(DomEventListener listener) {
return getElement().addEventListener("error", listener);
}
}