2.5. Page Navigation

Navigation between pages is achieved by using forwards, redirects and by setting the page template path.

2.5.1. Forward

To forward to another page using the servlet RequestDispatcher, set the Page's forward property. For example to forward to a page with a path index.htm:

/**
 * @see Page#onPost()
 */
public void onPost() {
    // Process form post
    ..

    setForward("index.htm");
}

This will invoke a new Page class instance mapped to the path index.htm.

Please note when a request is forwarded to another Page, the controls on the second page will not be processed. This prevents confusion and bugs, like a form on the second page trying to process a POST request from the first page.

2.5.1.1. Forward Parameter Passing

When you forward to another page the request parameters are maintained. This is a handy way of passing through state information with the request. For example you could add a customer object as a request parameter which is displayed in the template of the forwarded page.

public boolean onViewClick() {
    Long id = viewLink.getValueLong();
    Customer customer = CustomerDAO.findByPK(id);

    // Set the customer object as a request parameter
    getContext().setRequestAttribute("customer", customer);
    setForward("view-customer.htm");

    return false;
}

The snippet above forwards to the page template view-customer.htm:

<html>
 <head>
   <title>Customer Details</title>
 </head>
 <body>
   <h1>Customer Details</h1>
   <pre>
     Full Name: $customer.fullName
     Email:     $customer.email
     Telephone: $customer.telephone
    </pre>
  </body>
</html>

Request attributes are automatically added to the Velocity Context object so are available in the page template.

2.5.1.2. Page Forwarding

Page forwarding is another way of passing information between pages. In this case you create the page to be forwarded to using the Context createPage(String) method and then set properties directly on the Page. Finally set this page as the page to forward the request to. For example:

public boolean onEditClick() {
    Long id = viewLink.getValueLong();
    Customer customer = CustomerDAO.findByPK(id);

    // Create a new EditPage instance based on the specified path
    EditPage editPage = (EditPage) getContext().createPage("/edit-customer.htm");
    editPage.setCustomer(customer);
    setForward(editPage);

    return false;
}

When creating a page with the createPage() method, ensure you prefix the page path with the "/" character.

You can also specify the target page using its class as long as the Page has a unique path. Using this technique the above code becomes:

public boolean onEditClick() {
    Long id = viewLink.getValueLong();
    Customer customer = CustomerDAO.findByPK(id);

    // Create a new EditPage instance based on its class
    EditPage editPage = (EditPage) getContext().createPage(EditPage.class);
    editPage.setCustomer(customer);
    setForward(editPage);

    return false;
}

This Page forwarding technique is best practice as it provides you with compile time safety and alleviates you from having to specify page paths in your code. Please always use the Context createPage() methods to allow Click to inject Page dependencies.

Although uncommon it is possible to map more than one path to the same class. In these cases invoking Context createPage(Class) will throw an exception, because Click will not be able to determine which path to use for the Page.

2.5.2. Template Path

An alternative method of forwarding to a new page is to simply set the current Page's path to the new page template to render. With this approach the page template being rendered must have everything it needs without having its associated Page object created. Our modified example would be:

public boolean onViewClick() {
    Long id = viewLink.getValueLong();
    Customer customer = CustomerDAO.findByPK(id);

    addModel("customer", customer);

    // Set the Page's path to a new value
    setPath("view-customer.htm");

    return false;
}

Note how the customer object is passed through to the template in the Page model. This approach of using the Page model is not available when you forward to another Page, as the first Page object is "destroyed" before the second Page object is created and any model values would be lost.

2.5.3. Redirect

Redirects are another very useful way to navigate between pages. See HttpServletResponse. sendRedirect (location) for details.

The great thing about redirects are that they provide a clean URL in the users browser which matches the page that they are viewing. This is important for when users want to bookmark a page. The downside of redirects are that they involve a communications round trip with the users browser which requests the new page. Not only does this take time, it also means that all the page and request information is lost.

An example of a redirect to a logout.htm page is provided below:

public boolean onLogoutClick() {
    setRedirect("/logout.htm");
    return false;
}

If the redirect location begins with a "/" character the redirect location will be prefixed with the web applications context path. For example if an application is deployed to the context "mycorp" calling setRedirect("/customer/details.htm") will redirect the request to: "/mycorp/customer/details.htm".

You can also obtain the redirect path via the target Page's class. For example:

public boolean onLogoutClick() {
    String path = getContext().getPagePath(Logout.class);
    setRedirect(path);
    return false;
}

Note when using this redirect method, the target Page class must have a unique path.

A short hand way of redirecting is to simply specify the target Page class in the redirect method. For example:

public boolean onLogoutClick() {
    setRedirect(Logout.class);
    return false;
}

2.5.3.1. Redirect Parameter Passing

You can pass information between redirected pages using URL request parameters. The ClickServlet will encode the URL for you using HttpServletResponse.encodeRedirectURL (url).

In the example below a user will click on an OK button to confirm a payment. The onOkClick() button handler processes the payment, gets the payment transaction id, and then redirects to the trans-complete.htm page with the transaction id encoded in the URL.

public class Payment extends Page {
    ..

    public boolean onOkClick() {
        if (form.isValid()) {
            // Process payment
            ..

            // Get transaction id
            Long transId = OrderDAO.purchase(order);

            setRedirect("trans-complete.htm?transId=" + transId);

            return false;
        }
        return true;
    }
}

The Page class for the trans-complete.htm page can then get the transaction id through the request parameter "transId":

public class TransComplete extends Page {
    /**
     * @see Page#onInit()
     */
    public void onInit() {
        String transId = getContext().getRequest().getParameter("transId");

        if (transId != null) {

            // Get order details
            Order order = OrderDAO.findOrderByPK(new Long(transId));
            if (order != null) {
                addModel("order", order);
            }
        }
    }
}

2.5.3.2. Post Redirect

The parameter passing example above is also an example of a Post Redirect. The Post Redirect technique is a very useful method of preventing users from submitting a form twice by hitting the refresh button.