Vaadin supports two types of windows: application-level windows and sub-windows. The application-level windows are native browser windows or tabs. The main window is the special initial application-level window created when the user first started the application session by opening the URL in the browser. Sub-windows are freely floating HTML windows inside a browser window, as described in Section 4.3, “Sub-Windows”.

Application-level windows of the same application use the same Application object and therefore share the same session. Each window is identified with a URL that is used to access it. This makes it possible to bookmark application-level windows. Such windows can even be created dynamically based on the URLs.

Application-level windows allow several common use cases for browser-based applications.

  • Native popup windows. An application can open popup windows for sub-tasks.
  • Page-based browsing. The application can allow the user to open certain content to different windows. For example, in a messaging application, it can be useful to open different messages to different windows so that the user can browse through them while writing a new message.
  • Bookmarking. Bookmarks in the web browser can provide an entry-point to some content provided by an application.
  • Embedding windows. Windows can be embedded in web pages, thus making it possible to provide different views to an application from different pages or even from the same page, while keeping the same session. See Section 12.3, “Embedding Applications in Web Pages”.

Because of the special nature of AJAX applications, these uses require some caveats. We will go through them later in Section 12.2.5, “Caveats in Using Multiple Windows”.

Creating a new application-level window is much like creating a child window (see Section 4.3, “Sub-Windows”), except that the window is added with addWindow() to the application object instead of the main window.

public class WindowTestApplication extends Application {
    public void init() {
        // First create the main window.
        final Window main = new Window ("My Test Application");
        setMainWindow(main);
         
        // Create another application-level window.
        final Window mywindow = new Window("Second Window");
        
        // Manually set the name of the window.
        mywindow.setName("mywindow");
        
        // Add some content to the window.
        mywindow.addComponent(new Label("Has content."));

        // Add the window to the application.
        addWindow(mywindow);
    }
}

This creates the window object that a user can view by opening a URL in a browser. Creating an application-level window object does not open a new browser window automatically to view the object, but if you wish to open one, you have to do it explicitly as shown below. An application-level window has a unique URL, which is based on the application URL and the name of the window given with the setName() method. For example, if the application URL is http://localhost:8080/myapp/ and the window name is mywindow, the URL for the window will be http://localhost:8080/myapp/mywindow/. If the name of a window is not explicitly set with setName(), an automatically generated name will be used. The name can be retrieved with the getName() method and the entire URL with getURL().

There are three typical ways to open a new window: using the open() method of Window class, a Link, or referencing it from HTML or JavaScript code written inside a Label component.

The Window open() method takes as parameters a resource to open and the target name. You can use ExternalResource to open a specific URL, which you get from the window to be opened with the getURL() method.

/* Create a button to open a new window. */
main.addComponent(new Button("Click to open new window",
                  new Button.ClickListener() { 
    public void buttonClick(ClickEvent event) {
        // Open the window.
        main.open(new ExternalResource(mywindow.getURL()),
                  "_new");
    }      
}));

The target name is one of the default HTML target names (_new, _blank, _top, etc.) or a custom target name. How the window is exactly opened depends on the browser. Browsers that support tabbed browsing can open the window in another tab, depending on the browser settings.

Another typical way to open windows is to use a Link component with the window URL as an ExternalResource.

/* Add a link to the second window. */
Link link = new Link("Click to open second window",
                     new ExternalResource(mywindow.getURL()));
link.setTargetName("second");
link.setTargetHeight(300);
link.setTargetWidth(300);
link.setTargetBorder(Link.TARGET_BORDER_DEFAULT);
main.addComponent(link);

Using a Link allows you to specify parameters for the window that opens by clicking on the link. Above, we set the dimensions of the window and specify what window controls the window should contain. The Link.TARGET_BORDER_DEFAULT specifies to use the default, which includes most of the usual window controls, such as the menu, the toolbar, and the status bar.

Another way to allow the user to open a window is to insert the URL in HTML code inside a Label. This allows even more flexibility in specifying how the window should be opened.

// Add the link manually inside a Label.
main.addComponent(
    new Label("Second window: <a href='" + mywindow.getURL()
              + "' target='second'>click to open</a>",
              Label.CONTENT_XHTML));
main.addComponent(
    new Label("The second window can be accessed through URL: "
              + mywindow.getURL()));

When an application-level window is closed in the browser the close() method is normally called just like for a child window and the Window object is purged from the application. However, there are situations where close() might not be called. See Section 12.2.4, “Closing Windows” for more information.

You can create a window object dynamically by its URL sub-path when it is first requested by overriding the getWindow() method of the Application class. The method gets a window name as its parameter and must return the corresponding Window object. The window name is determined from the first URL path element after the application URL (the name may not contain slashes). See the notes below for setting the actual name of the dynamically created windows below.

The following example allows opening windows with a window name that begins with "planet-" prefix. Since the method is called for every browser request for the application, we filter only the requests where a window with the given name does not yet exist.

public class WindowTestApplication extends Application {
    ...

    @Override
    public Window getWindow(String name) {
        // If a dynamically created window is requested, but
        // it does not exist yet, create it.
        if (name.startsWith("planet-") &&
              super.getWindow(name) == null) {
            String planetName =
                    name.substring("planet-".length());

            // Create the window object.
            Window newWindow =
                    new Window("Window about " + planetName);
            
            // DANGEROUS: Set the name explicitly. Otherwise,
            // an automatically generated name is used, which
            // is usually safer.
            newWindow.setName(name);

            // Put some content in it.
            newWindow.addComponent(
                new Label("This window contains details about " +
                          planetName + "."));
            
            // Add it to the application as a regular
            // application-level window.
            addWindow(newWindow);
            
            return newWindow;
        }

        // Otherwise the Application object manages existing
        // windows by their name.
        return super.getWindow(name);
    }

The window name must be a unique indentifier for each Window object instance. If you use setName() to set the window name explicitly, as we did above, any browser window that has the same URL (within the same browser) would open the same window object. This is dangerous and generally not recommended, because the browser windows would share the same window object. Opening two windows with the same static name would immediately lead to a synchronization error, as is shown in Figure 12.1, “Synchronization Error Between Windows with the Same Name” below. (While also the window captions are same, they are irrelevant for this problem.)


There are some cases where setting the name explicitly is useful. The launch application below is one example, as it always opens the other windows in a window target that is specific to the window name, thereby never creating two windows with the same URL. Similarly, if you had embedded the application in a browser frame and the link would open the window in a frame, you would not have problems. Having a single window instance for a URL is also useful if the browser crashes and the user opens the window again, as it will have kept its previous (server-side) state.

Having multiple browser windows or tabs open in the same website and even the same page is one of the basic use cases of web browsing. The creation of Window objects described in the previous section allows opening multiple special-purpose windows with different URLs, but how to open multiple windows with the same URL? The solution is based on the fact that Vaadin doesn't identify windows only by their URL subpath, but also by an invisible window name.

Leaving the window name to be automatically generated allows opening multiple windows with the same URL, while each of the windows will have a separate state. The URL in the location bar stays unchanged and the generated window name is used only for the Ajax communications to identify the window object. A generated name is a string representation of a unique random number, such as "1928676448". You should be aware of the generated window names when overriding the getWindow() method (and not unintentionally create a new window instance dynamically for each such request). The condition in the above example would also filter out the requests for an already existing window with a generated name.

Figure 12.2, “A Dynamically Created Window” shows a dynamically created application-level window with the URL shown in the address bar. The URL for the application is here http://localhost:8080/book-examples/windowexample/, including the application context, application path. The dynamically created window's name is planet-mars.


The application knows the windows it already has and can return them after the creation. The application also handles closing and destruction of application-level window objects, as discussed in Section 12.2.4, “Closing Windows”.

Such dynamic windows could be opened as in the following example:

public void init() {
    final Window main = new Window("Window Test");
    setMainWindow(main);

    // Have some IDs for the dynamically creatable windows.
    final String[] items = new String[] { "mercury", "venus",
            "earth", "mars", "jupiter", "saturn", "uranus",
            "neptune" };
    
    // Create a list of links to each of the available window.
    for (int i = 0; i < items.length; i++) {
        // Create a URL for the window.
        String windowUrl = getURL() + "planet-" + items[i];
        
        // Create a link to the window URL. Using the 
        // item ID for the target also opens it in a new
        // browser window (or tab) unique to the window name.
        main.addComponent(
            new Link("Open window about " + items[i],
                     new ExternalResource(windowUrl),
                     items[i], -1, -1, Window.BORDER_DEFAULT));
    }
}

For cases where you need communication between windows, we recommend using floating child windows. In Vaadin Release 5, an application window can not update the data in other windows. The contents of a window can only be updated when the particular window makes a request to the server. The request can be caused by user input or through polling.

Changing the server-side state of a window while processing a user event from another window can potentially cause serious problems. Changing the client-side state of a window does not always immediately communicate the changes to the server. The server-side state can therefore be out of sync with the client-side state.


The following example creates a second window that changes the contents of the main window, as illustrated in the figure above. In this simple case, changing the main window contents is safe.

// Create a table in the main window to hold items added
// in the second window
final Table table = new Table();
table.setPageLength(5);
table.getSize().setWidth(100, Size.UNITS_PERCENTAGE);
table.addContainerProperty("Name", String.class, "");
main.addComponent(table);

// Create the second window
final Window adderWindow = new Window("Add Items");
adderWindow.setName("win-adder");
main.getApplication().addWindow(adderWindow);

// Create selection component to add items to the table
final NativeSelect select = new NativeSelect("Select item to add");
select.setImmediate(true);
adderWindow.addComponent(select);

// Add some items to the selection
String items[] = new String[]{"-- Select --", "Mercury", "Venus", 
        "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"};
for (int i=0; i<items.length; i++)
    select.addItem(items[i]);
select.setNullSelectionItemId(items[0]);

// When an item is selected in the second window, add
// table in the main window
select.addListener(new ValueChangeListener() {
    public void valueChange(ValueChangeEvent event) {
        // If the selected value is something else
        // but a null selection item.
        if (select.getValue() != null) {
            // Add the selected item to the table
            // in the main window
            table.addItem(new Object[]{select.getValue()},
                          new Integer(table.size()));
        }
    }
});

// Link to open the selection window
Link link = new Link("Click to open second window",
                     new ExternalResource(adderWindow.getURL()),
                     "_new", 50, 200,
                     Link.TARGET_BORDER_DEFAULT);
main.addComponent(link);

// Enable polling to update the main window
ProgressIndicator poller = new ProgressIndicator();
poller.addStyleName("invisible");
main.addComponent(poller);

The example uses an invisible ProgressIndicator to implement polling. This is sort of a trick and a more proper API for polling is under design. Making the progress indicator invisible requires the following CSS style definition:

.v-progressindicator-invisible {
    display: none;
}