3.4. Application-Level Windows

IT Mill Toolkit Release 5 introduces support for multiple application-level windows that can be used just like the main window. All such windows use the same application 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 URLs.

Application-level windows allow several uses important for usability of browser-based applications.

  • Native child windows. An application can open child windows that are not floating windows inside a parent window.
  • 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 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 3.8, “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 3.4.3, “Caveats in Using Multiple Windows”.

3.4.1. Creating New Application-Level Windows

Creating a new application-level window is much like creating a child window, 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() {
        final Window main = new Window ("Window Test Application");
        setMainWindow(main);
         
        /* Create a new 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("This is a second window."));

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

This creates the window object that a user can view by opening the URL in a browser. Creating a 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 a resource to open and the target. 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 HTML target names. 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, toolbar, and 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()));

-- This section is unfinished --


		

3.4.2. Creating Windows Dynamically

-- This section is unfinished --

3.4.3. Caveats in Using Multiple Windows

Closing Windows

Additional windows have the same problem as a single window: closing a window does not cause an event. If a user clicks on a close button of a window, the browser usually just closes it without running any JavaScript handlers for such an event. Therefore, the server will not know that the user closed the window and the server-side object will be left hanging.

If the window has a manually set URI, the user can reconnect to it using the URI.

Communication Between Windows

For cases where you need communication between windows, we recommend using floating child windows. In IT Mill Toolkit Release 5, an application window can not update the data in other windows. The contents of a window can only 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 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 server. The server-side state can therefore be out of sync with the client-side state.

Figure 3.3. Communication Between Two Application-Level Windows

Communication Between Two Application-Level Windows

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 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:

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