Friday, October 23, 2009

Page links with Wicket

The default urls created by Wicket do not look very nice. For instance, let IndexPage be the home page of our ShopApplication. Assuming shop is your application's context name and wicket the path where you mapped the Wicket filter, then by default, the url for the IndexPage will be akin to
http://somehost/shop/wicket/index?wicket:interface=:0:headerPanel:index2::ILinkListener::. I said ugly didn't I?

Wicket has a mechanism for prettifying URLs. In ShopApplication, you can instruct wicket to "mount" a page on a specific path. Here is the code.
public class ShopApplication extends WebApplication {
public Class getHomePage() {
return IndexPage.class;
protected void init() {
mountBookmarkablePage("index", IndexPage.class);
Wow, that was easy!

Now, assume you have a navigation panel, included on all the pages of your application, where you list links to the various pages of your application. Here is the markup:
<table cellpadding="0" cellspacing="0">
<tr align="center">
<td><a class="menuitem" href="index">Index</a></td>
... other pages
We are referencing the mounted path "index" directly. How convenient is that? Although, convenient such references do not work correctly. The navigator will translate it into http://somehost/shop/index instead of the correct http://somehost/shop/wicket/index.

For bookmarkable links such as "index", Wicket offers the <wicket:link> tag instructing Wicket to to automatically create bookmarkable
link components for the links inside the tags. So let's try that.
<tr align="center">
<td><a class="menuitem" href="index">Index</a></td>
... other pages
The above is easy enough and will work, as long as all your pages are in the same java package.

Indeed, the <wicket:link> tag doctors url references with respect to the java page of the current page belongs to, but if the page is located in a different package, then the urls will be rendered incorrectly. Bummer.

So, we seem to be stuck. Googling for "wicket url mountBookmarkablePage" you will find an article by R.J. Lorimer on controlling Wicket URLs which did not go far in reducing my level of confusion. After further investigation, I found an example which caters for the use case we've been discussing so far, that is the nice-url example.

The nice-url example, after mounting the Home page as "/the/homepage/path", simply adds a BookmarkablePageLink to the Home class on another page, namely Page1. Here is the relavant code.
<a wicket:id="homeLink" href="#">[go back]</a>

public class Page1 extends WicketExamplePage {
public Page1(PageParameters parameters) {
add(new BookmarkablePageLink("homeLink", Home.class));
which magically works, in the sense that when the link for the url is rendered, it will somehow contain the mounted path "/the/homepage/path". How does BookmarkablePageLink know that Home is mounted on "/the/homepage/path"?

It does not know. Instead, it invokes the
urlFor(IPageMap pageMap, Class pageClass, PageParameters parameters)
method defined in its parent class, Component. This method will in turn ask the request cycle the page is associated with to find the correct url. The request cycle will eventually go through all mount points, asking them whether they apply to Home.class. The first mount point which applies will then be asked to encode the Home page, returning the mounted path. The exact details might vary, but from a 100 feet point of view, wicket does reverse lookup to see which mount point maps to a given page which goes to explain the aforementioned magic.


Anonymous said...

Nice overview ... THX!

But is there no possibility to auto-link a mounted path without java code?! ...... i mean in case of different packages

i have multiple packages on the same level ...each package contains a page .... and each page is mounted to a specific path

i would like to auto-link PageAs mounpoint from PageB ...... any idea?
where Page A and B are in different packages on the same level

Anonymous said...

I have also been looking to use wicket:link in different packages, I don't think it's possible

andrewdriggs said...

Use href="package/page.html" to get outside of the current package. This is, of course, a relative path to the desired package.