Vaadin (Flow): Navigating to destination with a shared object
Asked Answered
A

3

5

Problem:

I currently have a grid that displays content of type SomeModel. When I click an entry of that Grid I would like to navigate to a view that takes an object as its input to display the entries content.

Implementation:

To achive this behaviour I created a DetailLayout like this:

public DetailLayout extends FlexLayout implements HasUrlParameter<SomeModel>{
    /* skipped some details */
    @Override
    public void setParameter(BeforeEvent event, Host parameter) {
        /* This is where I expected to be able to handle the object */
    }
}

From within the Grid I tried to navigate like this:

addSelectionListener((event) -> {
    event.getFirstSelectedItem().ifPresent(somemodel -> {
        getUI().ifPresent(ui -> {
            ui.navigate(DetailLayout.class, somemodel);
        });
    });
});

But unfortunately this behaviour is not supported by Vaadin even tho its syntax is perfectly fine.


Question:

Do you know of another way to pass an object while navigation or did I miss a certain part of the official documentation documentation ?

Thank you in advance

Azores answered 30/1, 2019 at 14:20 Comment(0)
J
6

Key-Value collection

As discussed in the comments on the other Answer, if you do not wish to expose the ID value as part of the URL, then work behind the scenes by using the key-value collection provided by Vaadin.

Vaadin actually provides key-value collections at three levels of scope:

  • Context
    Your entire web-app at runtime
  • Session
    Each user
  • UI
    Each web browser window/tab, as Vaadin supports multi-window web-apps

The app-wide key-value collection is available on the VaadinContext, via getAttribute & setAttribute methods.

VaadinService.getCurrent().getContext().setAttribute( key , value ) ;

The per-user key-value collection is available on the VaadinSession, via getAttribute & setAttribute methods.

VaadinSession.getCurrent().setAttribute( key , value ) ;

➥ The per-browser-window/tab collection (what you want for your needs in this Question) is not quite so readily available. You have to go through an indirect step. On the ComponentUtil class, call setData & getData methods. In addition to passing your key and your value, pass the current UI object.

Component c = UI.getCurrent() ;
String key = "com.example.acmeapp.selectedProductId" ;
Object value = productId ;
ComponentUtil.setData( c , key , value ) ;

Please vote for my ticket # 6287, a feature-request to add setAttribute/getAttribute methods on UI class, to match those of VaadinSession and VaadinContext.

Jonniejonny answered 22/8, 2019 at 7:16 Comment(7)
I prefer this answer over mine, as it does not expose the id in the url. More specifically, the user is not able to be sneaky and try out random productIds in the url. It's a little more complicated than my approach but well worth it in my opinion.Technique
@KasparScherrer Actually, your Answer is preferable in some scenarios where exposing the IDs in the URLs is permissible and possibly even desirable. For example, a product catalog where you may want the convenience of having the user (a customer or clerk) see, and perhaps even type, the product identifier in the URL. It all depends on the scenario’s business rules.Jonniejonny
While changing my detailview logic to use this approach I noticed that a new UI instance is being created when refreshing the page. The initial navigation from list-view to detail-view works, but as soon as I refresh the detail-view, the UI does not have the productId stored anymore. Therefore, storing the id in the Session is probably better, although this will probably introduce issues when having multiple tabs open.Technique
@KasparScherrer The UI object should be stable as long as the browser window/tab is open (by definition). You must have some other code involved, and you likely should fix it. Try a new throwaway project to verify behavior when storing attributes on the UI.Jonniejonny
@KasparScherrer Uh-oh. I am trying a new throwaway Vaadin 14.0.1 app, and I may be seeing what you reported: the UI object being replaced while routing. I'll explore further and report back here.Jonniejonny
@KasparScherrer Yes there seems to be some crazy behavior on the UI class in Vaadin 14.0.1. I posted a description and simple example app in the Vaadin Forums: vaadin.com/forum/thread/17810577Jonniejonny
Maybe this is why there's no setAttribute / getAttribute on the UI.. I am very interested in what the recommended way for this will be.Technique
T
5

Instead of giving the whole somemodel object as parameter of navigate(), you can pass its id

ui.navigate(DetailLayout.class, somemodel.getId());

And in the DetailLayout.setParameter() you can load the somemodel by its id

@Override
public void setParameter(BeforeEvent beforeEvent, Long someModelId) {
    if(someModelId == null){
        throw new SomeModelNotFoundException("No SomeModel was provided");
    }

    SomeModel someModel = someModelService.findById(someModelId);
    if(someModel == null){
        throw new SomeModelNotFoundException("There is no SomeModel with id "+someModelId);
    }

    // use someModel here as you wish. probably use it for a binder?
}
Technique answered 30/1, 2019 at 14:50 Comment(5)
Thank you for your reply. Thats actually the approach I tried to avoid as it introduces the need to check if the user actually has the right to see the element of the id he passes.Azores
That is true, any user could type into the url /somemodel/19 and hope that there is a somemodel with id 19. If there are somemodels that only certain users can see, then a check if they have the right is indeed due here.Technique
There are couple of other approaches as well. If you are using CDI or Spring, you could share the object as e.g. UIScoped bean. Also one alternative could be to share the object as session attribute.Louisville
@TatuLund Would you implement that by using a custom UI as described in the old vaadin documentation: vaadin.com/docs/v8/framework/articles/…Azores
@gsedlacz No, I would use ComponentUtil.setData(UI.getCurrent(),..) instead, see details: vaadin.com/api/platform/12.0.5/com/vaadin/flow/component/…Louisville
B
0

If you are using Spring with Vaadin Flow then you could create a @UIScoped bean and add your own fields storing state related to the current browser window/tab. The bean will be available as long as the UI is present.

Berget answered 4/2, 2020 at 13:21 Comment(1)
the problem is that the UI is not the same after navigation (see comments under accepted answer)Technique

© 2022 - 2024 — McMap. All rights reserved.