Vaadin 14 Nested Route avoid Refresh on navigate

Everytime I click on the “Add”-Button in the OperationsLayoutFactory, the Page is refreshed and empty after that.

I only want to insert the “textfieldName” into the Div in page1 of the tabs.

I can click Back in the Browser when i see the empty page. After that, its added correctly.

How can I avoid the loading of the new, empty page? Is there something wrong with my Routes?

Before Add-Click
[![Before “Add”-Click]
[1]
][1]

After Add-Click and Browser-Back
[![After Add-Click and Browser-Back]
[2]
][2]


**My Route Layout**
-UniversMainUI
--OperationsLayout (Route Name: Operations), Parent: UniversMainUI
---AddUniversitaetLayout (Route Name: justtest), Parent: OperationsLayoutFactory

UniversMainUI

@Route("ui")
@RouteAlias("")
@PageTitle("U N I V E R S")
@Theme(value = Lumo.class, variant = Lumo.DARK)
public class UniversMainUI extends Composite<VerticalLayout> implements HasComponents, RouterLayout {

@Autowired
private UniversLogoLayoutFactory universLogoLayoutFactory;

@Autowired
private UniversMenuFactory universMenuFactory;

Component logo;
Component menueLinks;
Component rechtsDatenfeld;
Component studentRemove;

private Div childWrapper = new Div();

public UniversMainUI() {

}

@PostConstruct // autowired-Components sind erst nach Konstruktordurchlauf verfuegbar
private void init() {

	logo = universLogoLayoutFactory.createComponent();
	add(logo);

	menueLinks = universMenuFactory.createComponent();
	add(menueLinks);


	Stream<Component> children = menueLinks.getChildren();
	List<Component> listchildren = children.collect(Collectors.toList());
	ArrayList<Component> arraylistchildren = new ArrayList<Component>(listchildren);

	HorizontalLayout h1 = new HorizontalLayout(menueLinks, childWrapper);
	h1.setHeight("75%");
	h1.setWidth("100%");
	getContent().setSizeFull(); // wichtig fuer Ermoeglichung prozentualer Angabe der Komponentenausmasse
	add(h1);

	for (Component c : arraylistchildren) {
		if (c.getId().get().equals("menueTreeGrid")) // auf Werte vom Typ Optional kann mittels name.get zugegriffen werden
		{
			TreeGrid<Ebene> myTreeGrid = (TreeGrid<Ebene>) arraylistchildren.get(0);
			myTreeGrid.addItemClickListener(event -> {
				if (event.getItem().getBezeichnung().equals(LangStrings.MENU_ADD_STUDENT.toString())) {
					myTreeGrid.getUI().ifPresent(ui -> ui.navigate("StudentLayout"));
				}
				if (event.getItem().getBezeichnung().equals(LangStrings.MENU_REMOVE_STUDENT.toString())) {
					myTreeGrid.getUI().ifPresent(ui -> ui.navigate("DeleteStudent"));
				}
				if (event.getItem().getBezeichnung().equals(LangStrings.MENU_OPERATIONS.toString())) {
					myTreeGrid.getUI().ifPresent(ui -> ui.navigate("Operations"));
				}

				;
			});
		}
	}
}

@Override
public void showRouterLayoutContent(HasElement content) {
	childWrapper.getElement().appendChild(content.getElement());
}
}

OperationsLayout

@org.springframework.stereotype.Component // spring-Component und NICHT vaadin-component (aufpassen bei Import)
public class OperationsLayoutFactory extends Composite<VerticalLayout> implements 
UIComponentBuilder,RouterLayout {
@Autowired
AddUniversitaetLayoutFactory addUniversitaetLayoutFactory;
Div childChildWrapper=new Div();

@ParentLayout(UniversMainUI.class)
@Route(value = "Operations", layout = UniversMainUI.class)
private class OperationsLayout extends VerticalLayout {
	Tab tab1;
	Tab tab2;
	Tab tab3;
	Div page1;
	Div page2;
	Div page3;
	Map<Tab, Component> tabsToPages;
	Tabs tabs;
	Div pages;
	
	Button addButton;

	public OperationsLayout() {
		init().layout();
	}
	

	public OperationsLayout init() {
		tab1 = new Tab("Hinzufuegen");
		tab2 = new Tab("Alle Anzeigen");
		tab3 = new Tab("Statistiken");

		page1 = new Div();
		page2 = new Div();
		page3 = new Div();

		

		tabsToPages = new HashMap<>();

		tabs = new Tabs(tab1, tab2, tab3);
		pages = new Div(page1, page2, page3);
		
		
		addButton=new Button("Add");
		addButton.addClickListener(e -> {
			page1.getUI().ifPresent(ui -> ui.navigate("justtest"));
		});
		


		return this;
	}

	public OperationsLayout layout() {

		page1.setText("Erste Seite");
		page1.add(addButton);

		page2.setText("Zweite Seite");
		page2.setVisible(false);

		page3.setText("Dritte Seite");
		page3.setVisible(false);

		tabsToPages.put(tab1, page1);
		tabsToPages.put(tab2, page2);
		tabsToPages.put(tab3, page3);

		pages.setWidth("100%"); // Nutzung verfuegbarer Breite fuer Tabinhalte
		tabs.setWidth("100%");
		tabs.setFlexGrowForEnclosedTabs(1);

		Set<Component> gezeigteSeiten = Stream.of(page1).collect(Collectors.toSet());

		tabs.addSelectedChangeListener(event -> {
			gezeigteSeiten.forEach(page -> page.setVisible(false));
			gezeigteSeiten.clear();
			Component ausgewaehlteSeite = tabsToPages.get(tabs.getSelectedTab());
			ausgewaehlteSeite.setVisible(true);
			gezeigteSeiten.add(ausgewaehlteSeite);
		});

		HorizontalLayout h1=new HorizontalLayout();
		childChildWrapper.setWidth("100%");
		childChildWrapper.setHeight("100%");
		h1.add(childChildWrapper);
		page1.add(h1);
		setSizeFull();
		add(tabs);
		add(pages);
		return this;
	}

}

@Override
public com.vaadin.flow.component.Component createComponent() {

	return new OperationsLayout().init().layout();

}

@Override
public void showRouterLayoutContent(HasElement content) {
	System.out.println("zu childchildwrapper: "+childChildWrapper.getElement()+" wird jetzt hinzugefuegt: "+content.getElement());
	childChildWrapper.getElement().appendChild(content.getElement());
}
}

AddUniversitaetLayout

@org.springframework.stereotype.Component // spring-Component und NICHT vaadin-component (aufpassen bei Import)
public class justTest extends Composite<VerticalLayout> implements UIComponentBuilder {	//TODO: Listener einbauen

@Route(value="justtest", layout=OperationsLayoutFactory.class)
private class AddUniversitaetLayout extends Composite<VerticalLayout>{
	TextField textfieldName;
	
	public AddUniversitaetLayout() {
		// TODO Auto-generated constructor stub
		init().bind().layout();
	}


	public AddUniversitaetLayout init() {

		textfieldName=new TextField("Test erfolgreich!");
		return this;
	}

	public AddUniversitaetLayout bind() {
		return this;
	}

	public AddUniversitaetLayout layout() {
		
		
		getContent().add(textfieldName);
		return this;

	}

}

@Override
public com.vaadin.flow.component.Component createComponent() {
	return new AddUniversitaetLayout().init().bind().layout();
}

[1]
: https://i.stack.imgur.com/ZbsDm.jpg
[2]
: https://i.stack.imgur.com/6pizP.jpg

Push :slight_smile:

I did not read your code, but FYI:

Try @PreserveOnRefresh

To survive a click on the web browser’s Reload button, you must use the @PreserveOnRefresh annotation. You may need to do so as well for the browser’s Back button. I’m not sure, but you can give it a try. See item # 6 here:
https://vaadin.com/blog/top-14-new-features-in-vaadin-14

When trying this annotation, be careful to read the Preconditions and Limitations listed at end of [page in the manual]
(https://vaadin.com/docs/v14/flow/advanced/tutorial-preserving-state-on-refresh.html#preconditions-and-limitations). This annotation does not preserve the UI instance, as that is quite often replaced/reinitialized. See [Issue # 6,321]
(https://github.com/vaadin/flow/issues/6321) for some discussion.

Each route invocation loads a new view

As for the routing, I am not familiar with [nested routing]
(https://vaadin.com/docs/v14/flow/routing/tutorial-router-layout.html). But I know for top-level URL in browser address bar, the use of a Route does indeed load a fresh view each time. My understanding is that this behavior is an intentional feature, and not a bug.

The [documentation talks]
(https://vaadin.com/docs/v14/flow/routing/tutorial-routing-lifecycle.html) about the BeforeLeaveEvent as a way to handle this departure where user might lose data:

A typical use case for this event is to ask the user whether they want to save any unsaved changes before navigating to another part of the application.