In Vaadin 8, we can let the user open a new tab/window within their web browser by clicking a button that has been associated with a BrowserWindowOpener. As discussed in the man
There are basically three approaches you can use, and combinations of these
Use URI parameters. As you mentioned, this is limited to String
type data.
String uriFragment = Page.getCurrent().getUriFragment();
VaadinRequest.getParameter()
, VaadinRequest is given as parameter in init(...) of main UIUI's in different browser tabs share the same Vaadin session. That gives some tools, namely
You can use session attributes, i.e. VaadinSession.getCurrent().getAttribute(…)
and VaadinSession.getCurrent().setAttribute(…)
If you use CDI or Spring, you can Inject / Autowire @VaadinSessionScoped
bean. The instance is then bound to Session and hence shared between the tabs.
Read data from database (possibly using 1. and/or 2. as help for keys)
The Answer by Tatu Lund mentioned passing a URI parameter to the new window being opened.
Here is a simple little demonstration app in Vaadin 8.5.0 to show that technique.
We start with three cats that we pretend to retrieve from a database. Each time the user selects a cat, we get the cat’s identifier, a UUID object. We generate a canonical 32-character hex string representation of that UUID. And we specify that as the parameter value to be passed with the parameter key cat_id
. We specify that parameter by calling BrowserWindowOpener::setParameter
.
Note that we do this on the selection of an item in the Grid
listing cats. Because of browser restrictions in opening windows, the BrowserWindowOpener
must be configured before the user clicks its button. We cannot run code in reaction to the button click, as far as I know.
The new browser window is populated with an automatically-instantiated subclass of UI. In this case CatUI
is what we wrote. In CatUI
, we get the URI parameter, extract the string representing the UUID, recreate the UUID
object, and then pass that to our pretend database-service to fetch the cat in question. Then a layout’s fields are populated with the Cat
object values. We don’t bother with data-binding the cat-to-layout as that is not the point of this demo.
Caveat: This is just a demo, and ignores issues that are not directly related to issue of passing information via URI parameter to a new window. For example, crucial issues of concurrency are ignored here but would not be in real work.
This demo consists of four class files, all pasted below.
MainUI
(opened by default when app launches)CatUI
(opened when user clicks button)Cat
(business object, with name
and id
properties)DatabaseService
(pretend storehouse of cat records)MainUI
package com.basilbourque.example;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.BrowserWindowOpener;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Grid;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import javax.servlet.annotation.WebServlet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
/**
* This UI is the application entry point. A UI may either represent a browser window
* (or tab) or some part of an HTML page where a Vaadin application is embedded.
* <p>
* The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be
* overridden to add component to the user interface and initialize non-component functionality.
*/
@Theme ( "mytheme" )
public class MainUI extends UI {
private Grid< Cat > grid;
private Button button;
@Override
protected void init ( VaadinRequest vaadinRequest ) {
// Configure button to open now browser window/tab.
this.button = new Button( "Open in new window" );
BrowserWindowOpener opener = new BrowserWindowOpener( CatUI.class );
opener.setFeatures( "height=300,width=500,resizable" );
opener.extend( this.button );
opener.setParameter( "cat_id" , new UUID(0,0).toString()); // Send nil UUID (all zeros) if nothing selected.
System.out.println( "BWO URL: " + opener.getUrl() );
this.button.setEnabled( false );
this.grid = new Grid<>( Cat.class );
this.grid.setCaption( "Cats" );
List<Cat> cats = new DatabaseService().fetchAllCats() ;
this.grid.setItems( cats );
// Every time the user selects a cat in the Grid, assign that cat’s ID to our `BrowserWindowOpener`. This way our button is always prepared to open a window for the selected cat.
this.grid.addSelectionListener( event -> {
Set< Cat > selectedCats = event.getAllSelectedItems();
this.button.setEnabled( selectedCats.size() > 0 );
if ( selectedCats.size() > 0 ) { // If the user selected an item.
Cat cat = selectedCats.stream().findFirst().get();
opener.setParameter( "cat_id" , cat.getId().toString() ); // A UUID’s canonical presentation is as a 36-character hexadecimal string in five groups with HYPHEN-MINUS as delimiter.
} else {
opener.setParameter( "cat_id" , new UUID(0,0).toString()); // Send nil UUID (all zeros) if nothing selected.
}
System.out.println( "BWO URL: " + opener.getUrl() );
} );
this.grid.select( cats.stream().findFirst().get() ); // Select first item arbitrarily, to provoke the grid’s selection-listener above to fire.
button.addClickListener( e -> {
System.out.println( "BASIL opening now window" );
} );
final VerticalLayout layout = new VerticalLayout();
layout.addComponents( this.grid , button );
setContent( layout );
}
@WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
@VaadinServletConfiguration ( ui = MainUI.class, productionMode = false )
public static class MyUIServlet extends VaadinServlet {
}
}
CatUI
package com.basilbourque.example;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.*;
import java.util.Optional;
import java.util.UUID;
public class CatUI extends UI {
private Cat cat = null;
@Override
protected void init ( VaadinRequest vaadinRequest ) {
// Retrieve a parameter from the URI of this UI/window.
String catUuidString = vaadinRequest.getParameter( "cat_id" ); // In the URI key-value parameters, "cat_id" is our key, and a UUID’s hex string is the expected value.
if ( null == catUuidString ) { // If we did not receive the UUID-string parameter we expected.
this.setContent( this.buildLayoutForNoCat( null ) );
return;
}
UUID uuid = UUID.fromString( catUuidString ); // Rehydrate the `UUID` from our passed hex string representing the UUID’s value.
Optional< Cat > cat = new DatabaseService().fetchCat( uuid );
if ( cat.isPresent() ) { // NULL check.
System.out.println( "uuidString: " + uuid + " and cat: " + cat.get() );
this.setContent( this.buildLayoutForCat( cat.get() ) ); // Retrieve the `Cat` object from our `Optional< Cat >` object by calling `get()` only after checking for NULL.
return;
} else { // Failed to find cat.
this.setContent( this.buildLayoutForNoCat( uuid ) );
return;
}
}
private Layout buildLayoutForCat ( Cat cat ) {
this.cat = cat ;
this.getPage().setTitle( "Cat details" );
// Have some content for it
TextField name = new TextField( "Name: " );
name.setWidth( 100 , Unit.PERCENTAGE );
name.setValue( this.cat.getName() );
TextField id = new TextField( "Id: " );
id.setWidth( 100 , Unit.PERCENTAGE );
id.setValue( this.cat.getId().toString() );
VerticalLayout layout = new VerticalLayout();
layout.addComponent( name );
layout.addComponent( id );
return layout;
}
private Layout buildLayoutForNoCat ( UUID uuid ) {
VerticalLayout layout = new VerticalLayout();
String message = "No cat found for the id: " + uuid;
Label label = new Label( message );
layout.addComponentsAndExpand( label );
return layout;
}
}
Cat
package com.basilbourque.example;
import java.util.UUID;
public class Cat {
private UUID id;
private String name;
public Cat ( UUID id , String name ) {
this.id = id;
this.name = name;
}
public UUID getId () {
return id;
}
public void setId ( UUID id ) {
this.id = id;
}
public String getName () {
return name;
}
public void setName ( String name ) {
this.name = name;
}
// Override `Object`.
@Override
public String toString () {
return "Cat{ " +
"id=" + id +
", name='" + name + '\'' +
" }";
}
}
DatabaseService
package com.basilbourque.example;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
// Pretending to be our gateway to a database.
public class DatabaseService {
static private List< Cat > cats;
{
DatabaseService.cats = List.of( // Produces an unmodifiable list. (A new feature in Java 9 and later.)
new Cat( UUID.fromString( "adf5c1a0-912e-11e8-9eb6-529269fb1459" ) , "Fluffy" ) ,
new Cat( UUID.fromString( "d37401c6-912e-11e8-9eb6-529269fb1459" ) , "Spot" ) ,
new Cat( UUID.fromString( "de29b6d8-912e-11e8-9eb6-529269fb1459" ) , "Lilly Mae" )
);
}
public List< Cat > fetchAllCats () {
return new ArrayList<>( DatabaseService.cats ); // Copy the list, then return.
}
public Optional< Cat > fetchCat ( UUID uuid ) {
return DatabaseService.cats.stream().filter( cat -> cat.getId().equals( uuid ) ).findFirst();
}
public static void main ( String[] args ) {
Optional< Cat > cat = new DatabaseService().fetchCat( UUID.fromString( "de29b6d8-912e-11e8-9eb6-529269fb1459" ) );
if ( cat.isPresent() ) {
System.out.println( "cat: " + cat.get() );
} else {
System.out.println( "No cat found." );
}
}
}