1

I have a situation in my application where I have a view page for an entity. These entities can be either by themselves, or link to each other as parent/child. In a situation where I'm viewing a child, I want the user to simply press a button to navigate to the parent. This entails opening up the exact same page via the same route. However, navigate does not reload the page when navigating to itself. I need it to, because I'm dealing with dozens of fields and submodules that all need to be rebuilt and repopulated. Presently, what I'm doing is this:

if (myEntity.getParent() != null) { myMenu.add(new Button("View Parent", evt -> /*UI.getCurrent().navigate( MyEntityViewUI.class, new RouteParameters(HasUrlParameterFormat.PARAMETER_NAME, String.valueOf(myEntity.getParent().getId())) )*/ UI.getCurrent().getPage().setLocation( GeneralUtils.getAnnotationForClass(MyEntityViewUI.class, Route.class).value() + "/" + myEntity.getParent().getId() ) )); } 

The commented out portion is what I'd like to do. The setParameter method is invoked, but not the constructor and thus not any of the logic I've currently implemented that actually builds my page. And even if I manually invoked that, I'd have to rewrite 2 layers of abstract parents to properly gut my existing components so that I can just rebuild everything fresh. I'd really prefer to not have to do that if it can be avoided.

The current code I have is using setLocation, but this is hackey and isn't a smooth transition like navigate() is. Plus, I can't just provide the class and RouteParameters via this, hence the manual construction of the string.

Is it possible to force Vaadin to perform a full page reload when navigating to the same Route? I don't really understand why it wouldn't be re-instantiating the navigation target.

I'm using Vaadin 24.6.2.


To clarify, I'm not building things within the constructor. I am utilizing a general BeforeEnter listener in my abstract super class which invokes an abstract initUI() method that is overridden by implementation classes. Part of the problem is that my button and menu in the above is done inside of a customized menu put into the AppLayout's Drawer pane, and even the HasDynamicTitle is utilizing information from the viewed entity.

1 Answer 1

0

You are doing all your work in the c'tor of your view -- but Vaadin Flow only instantiates views when needed (e.g. when navigating to a different view). So this is to be expected and instead of trying to hack around that, you are expected to do the bulk of the work not in the c'tor, but in the navigation callbacks.

  1. So build your fixed content for the view in the c'tor (things, that never change).
  2. Then implement HasUrlParameter and friends if you need to.
  3. Finally implement AfterNavigationObserver and do everything there.

The two callbacks are always called for each navigation into this view in this order. In the afterNavigation use the information previously gathered in setParameter and/or from the given event to update the UI, even if this means to remove everything and add something new.

From a separation-of-concerns point of view, it generally makes sense to not put too much "UI" into the view. The main concern here is, integration into routing, layout-nesting, maybe authorization, …

@Route(value = "") class MainView() : FlexLayout(), HasUrlParameter<String>, AfterNavigationObserver { val paramDisplay: TextField = TextField() val mainDisplay: TextField = TextField() init { flexDirection = FlexDirection.COLUMN addClassName(LumoUtility.Gap.MEDIUM) add( paramDisplay, mainDisplay.apply { value = "Init" }, Button("Nav").apply { addClickListener { ui.ifPresent { it.navigate(MainView::class.java, counter.getAndIncrement().toString()) } } } ) } override fun setParameter(event: BeforeEvent?, parameter: String?) { paramDisplay.value = parameter ?: "n/a" } override fun afterNavigation(event: AfterNavigationEvent?) { mainDisplay.value = Instant.now().toString() } } 
Sign up to request clarification or add additional context in comments.

6 Comments

Well, I am utilizing the listeners. I do in fact have a initUI() method that is invoked by a beforeEnter listener in my parent abstract, since that happens after the setParameter which I want customized in my child class. I'd already found that the UI kept rebuilding and repeating if the AppLayout drawer's navigation item was clicked multiple times, so I'd added in a uiCreated boolean to my init method to prevent duplication. But what you seem to be implying is that I should instead do a removeAll instead? But then how do I account for the Drawer nav and such? How do I clean it?
First before is meant to prevent navigation -- after is for "we are here now and do the work". Yes, you can just remove the old content and add the new, if this is the easiest way. Or you could write your content so it's "updatable" (e.g. updating a form could be as easy as setting a value on the binder). And for the "stable" parts of the application you usually want to have a parent for the view. All in all this sounds alot like X-Y-problems; so providing a minimal reproducible example would really help to help you fighting your real problem.
I really cannot do a bean read, as these entities have about a dozen collections of further entities, so there are several grids for displaying and allowing editing of these collections, and I also have sections where inputs are dynamically generated by looping through these collections. These are not set from the bean, but via separate Repository calls that reference the bean's ID - Lazy Initialization and all that.
Additionally, Min-Repoducible with the bigger picture and not just a simple example of the basic problem is going to be tricky. The view consists of about 35 interwoven class files by my rough count. I'll see what I can do to write something up, but I don't think it really changes my question.
This sounds very convoluted (e.g. DB concerns bleeding in your UI). If you really can't help it and reloading the whole page is all you can do, then I'd say, that this is a duplicate of stackoverflow.com/a/52112708/3181392 ?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.