I was trying to pass two SvgIcon
from MainView
to separate Java class file which are tab contents, so I could change the icon but I couldn't find the element implementing a function like this:
public SvgIcon getSvgIcon(String classSvgIcon) {
return (SvgIcon) CurrentInstance.get(SvgIcon.class).findAncestor(SvgIcon.class).hasClassName(classSvgIcon);
}
because I supposed it should return me the SvgIcon
element that corresponds to the class name contained in the element, even if I'd prefer the ID but I see there is no hasIdAttribute()
function but it didn't worked...
so then I tried to pass the MainView UI class to access directly to the component I want to change, like this:
public static MainView getCurrent() {
return (MainView) CurrentInstance.get(UI.class);
}
but even if in the tab content class I can call now MainView.getCurrent().myIconElement
but in the function it says Cannot cast from UI to MainView
... how it should be rewritten correctly that function so I could access to that svg icon I want to change?
Thanks in advance to all! Cheers!
[EDIT]
There is my sample I'm trying to implement to change icon...
/* ************************************************************
* *
* MainView class *
* *
************************************************************ */
package com.myexample.application.views.main;
import java.util.HashMap;
import java.util.Map;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.NativeLabel;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.SvgIcon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.tabs.Tab;
import com.vaadin.flow.component.tabs.Tabs;
import com.vaadin.flow.component.tabs.TabsVariant;
import com.vaadin.flow.internal.CurrentInstance;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.HighlightConditions;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouterLayout;
import com.vaadin.flow.router.RouterLink;
import com.vaadin.flow.server.StreamResource;
@PageTitle("Main")
@Route("")
public class MainView extends VerticalLayout implements RouterLayout, BeforeEnterObserver {
private Map<Tab, RouterLink> tabSheets = new HashMap<>();
private Tabs mainTabs;
private HorizontalLayout toolbar;
private NativeLabel appname;
public SvgIcon iconToChange;
public MainView() {
RouteTabs tabsToAdd = new RouteTabs();
tabsToAdd.addTab(new RouterLink("Tab 1", MyTab1.class), VaadinIcon.EYE.create());
tabsToAdd.addTab(new RouterLink("Tab 2", MyTab2.class), VaadinIcon.EYE.create());
add(getToolbar(), tabsToAdd);
}
@Override
public void beforeEnter(BeforeEnterEvent event) {
if (event.getNavigationTarget() == MainView.class) {
event.forwardTo(MyTab1.class);
}
}
public static MainView getCurrent() {
return (MainView) CurrentInstance.get(UI.class).getCurrent();
//return CurrentInstance.get(UI.class);
//return UI.getCurrent();
}
public SvgIcon getIconToChange() { return (SvgIcon) iconToChange; }
private HorizontalLayout getToolbar() {
HorizontalLayout leftLayout = new HorizontalLayout();
appname = new NativeLabel("My Test");
appname.setClassName("title-style");
leftLayout.add(appname);
leftLayout.setAlignItems(FlexComponent.Alignment.START);
leftLayout.setWidth("100%");
HorizontalLayout rightLayout = new HorizontalLayout();
StreamResource svgIconResource = new StreamResource("myOff_Icon.svg",
() -> getClass().getResourceAsStream("/img/myOff_Icon.svg"));
svgIconResource.setCacheTime(0);
iconToChange = new SvgIcon(svgIconResource);
iconToChange.addClassName("iconToChange");
iconToChange.setId("iconToChange");
iconToChange.setTooltipText("IconToChange");
rightLayout.add(iconToChange);
rightLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
rightLayout.setWidth("100%");
var toolbar = new HorizontalLayout(leftLayout, rightLayout);
toolbar.setWidthFull();
toolbar.addClassName("toolbar");
return toolbar;
}
private static class RouteTabs extends Tabs implements BeforeEnterObserver {
private final Map<RouterLink, Tab> routerLinkTabMap = new HashMap<>();
public void addTab(RouterLink routerLink, Icon iconToAdd) {
routerLink.setHighlightCondition(HighlightConditions.sameLocation());
routerLink.setHighlightAction(
(link, shouldHighlight) -> {
if (shouldHighlight) setSelectedTab(routerLinkTabMap.get(routerLink));
}
);
routerLinkTabMap.put(routerLink, new Tab(iconToAdd, routerLink));
add(routerLinkTabMap.get(routerLink));
this.addThemeVariants(TabsVariant.LUMO_EQUAL_WIDTH_TABS);
this.setMaxWidth("100%");
this.setWidthFull();
}
@Override
public void beforeEnter(BeforeEnterEvent event) {
// In case no tabs will match
setSelectedTab(null);
}
}
/*public SvgIcon getSvgIcon(String idSvgIcon) {
//return (SvgIcon) CurrentInstance.get(SvgIcon.class).equals(getElement().getAttribute("id").equals(idSvgIcon));
//return (SvgIcon) CurrentInstance.get(SvgIcon.class).findAncestor(SvgIcon.class).hasClassName(idSvgIcon);
return (SvgIcon) CurrentInstance.get(SvgIcon.class).getElement().hasAttribute("id");
} */
}
/* ************************************************************
* *
* MainView class *
* *
************************************************************ */
/* ************************************************************
* *
* MyTab1.class class *
* *
************************************************************ */
package com.myexample.application.views.main;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.icon.SvgIcon;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamResource;
@Route(value="mytab1", layout = MainView.class)
public class MyTab1 extends VerticalLayout {
/**
*
*/
private SvgIcon iconChangeTo;
private Text localText;
private Button localButton;
public ARDevices() {
localText = new Text("My Tab 1");
localButton = new Button("Change Icon");
localButton.addClickListener(e -> {
//iconChangeTo = Element.getAttribute("id").
//iconChangeTo = UI.getCurrent().navigate(SvgIcon.class).ifPresent(icon -> icon.hasClassName("iconToChange")).
//SvgIcon icon = ((MainView) MainView.getCurrent()).getIconToChange();
//MainView.getCurrent()
SvgIcon icon = MainView.getCurrent().getIconToChange()
//UIclass.iconToChange.setSrc( new StreamResource("myON_Icon.svg",
// () -> getClass().getResourceAsStream("/img/myON_Icon.svg")) );
});
add(localText, localButton);
}
}
/* ************************************************************
* *
* MyTab1.class class *
* *
************************************************************ */
There's three ways that I'm aware of to get the MainView instance from a child view.
First option is to get the current UI instance and find the mainView within its children:
MainView mainView = (MainView) UI.getCurrent().getChildren()
.filter(component -> component.getClass() == MainView .class).findFirst().orElse(null);
Second option is similar but vice versa, you start at the child view and look upwards. It looks uglier since you can only get 1 parent at a time in contrast to getChildren which returns a filterable Stream. I'm also unsure if this works within the constructor of the child view:
MainView mainView = null;
Optional<Component> parentOpt = this.getParent(); // this == some child view
while(parentOpt.isPresent()) {
Component parent = parentOpt.get();
if(parent instanceof MainView) {
mainView = (MainView) parent;
parentOpt = Optional.empty(); // stop the loop
} else {
// look further
parentOpt = parent.getParent();
}
}
And finally the third option, which is what I use, but it requires dependency injection. In both the MainView as well as all possible child views that need it, we inject a MainViewBus
(not so sure about the naming.. Find a better name for it if you can), which we can use to get the MainView directly. No HTML element child/parent crawling necessary here.
@Component
@UIScope
public class MainViewBus {
private MainView mainView;
public MainViewBus() {
}
public MainView getMainView() {
return this.mainView;
}
public void setMainView(MainView mainView) {
this.mainView = mainView;
}
}
public class MainView implements RouterLayout {
public MainView(MainViewBus mainViewBus){
mainViewBus.setMainView(this);
}
public void someMethod(){
Notification.show("You just invoked a method on MainView from a child view.");
}
}
@Route("foo", layout = MainView.class)
public class FooView extends VerticalLayout {
public FooView(MainViewBus mainViewBus){
add(new Button("Test MainView Bus", click -> mainViewBus.getMainView().someMethod()));
}
}