Aug 20, 2009

Page Title in Wicket

For a simple Wicket webapp, setting page title can be as easy as using the wicket:message tag, like this:


<!DOCTYPE html>
<html>
<head>
<title><wicket:message key="page.title"></wicket:message></title>
</head>
...
</html>


Where in the .property file, related to each page, the page.title property can be set to whatever the page is titled.

When you have a complex application, however, you might need to have more control over the page title, specifically when the title has to be dynamically generated depending on, e.g., product name.

There might be different approaches to this, but the one I'm using allows to have a default title for all pages which can be overriden for each particular page.

Most of the times, there's a base page for all the other pages. The pageTitle label is added to the page, HTML placeholder for which is the <title> tag.

BasePage.html

<!DOCTYPE html>
<html>
<head>
<title wicket:id="pageTitle"></title>
</head>
...
</html>


Then we add the component to the page.
The idea is to have a quick method to replace the pageTitle component model once the data required to construct title is loaded. So the setPageTitle(IModel model) method is added.

BasePage.java

public class BasePage {

public BasePage() {
add(new Label("pageTitle", new StringResourceModel("page.title", this, null)));
}

protected void setPageTitle(IModel model) {
get("pageTitle").setModel(model);
}
}


Now, in the ProductPage that extends the base page, we fetch the product data and update the page title:

ProductPage.java


public class ProductPage {

final int id;

public ProductPage(PageParameters params) {
id = params.getInt("id");
final Product p = productDao.get(id);
...
// add components or whatever
...
setPageTitle(new StringResourceModel("pageTitle", this, new Model(p)));
}
}


The ProductPage.properties has also to be updated, assuming the product has getTitle() and getPrice() methods:

ProductPage.properties

page.title=Shiny ${title} for just ${price}!


That's the basic idea. I'm not particularly happy with this approach as it requires to always remember to update the pageTitle component and push the title into the page. I'd actually prefer a way to override something so the title would be pulled for me. Ideally, that would look like this:


add(new Label("pageTitle", getPageTitle()));


Unfortunately, in this case if the label is added on the BasePage, even if the getPageTitle() is overriden in the ProductPage, since BasePage is constructed prior to any logic found in ProductPage, there is no data to return in getPageTitle().

5 comments:

Somatik said...

Thanks for the post!

Unknown said...

Thanks for the post. I made 'getPageTitle' abstract and in the constructor I initialize the label with getPageTitle so i force every extended class to implement the getPageTitle.

Anonymous said...

Have you thought of this pattern?

Basically, you use a getter, not a setter.

I make it return a String (not a Component) and have some default text for my Label in case the String is null.
By that I have a nice fallback when I forget to set an individual page title.

Unknown said...

Starting from Wicket 1.4 (?) components can be initialize in an onInitialize() method which safely can use overridden/abstract methods.

Unknown said...

...thanks for a tip with Label.