[TOC]

6. Keeping control over HTML

Many Wicket newbies are initially scared by its approach to web development because they have the impression that the component-oriented nature of the framework prevents them from having direct control over the generated markup. This is due to the fact that many developers come from other server-side technologies like JSP where we physically implement the logic that controls how the final HTML is generated.

This chapter will prevent you from having any initial misleading feeling about Wicket showing you how to control and manipulate the generated HTML with the built-in tools shipped with the framework.

6.1. Hiding or disabling a component

At the end of the previous chapter we have seen how to hide a component calling its method setVisible. In a similar fashion, we can also decide to disable a component using method setEnabled. When a component is disabled all the links inside it will be in turn disabled (they will be rendered as ) and it can not fire JavaScript events.

Class Component provides two getter methods to determinate if a component is visible or enabled: isVisible and isEnabled.

Even if nothing prevents us from overriding these two methods to implement a custom logic to determinate the state of a component, we should keep in mind that methods isVisible and isEnabled are called multiple times before a component is fully rendered. Hence, if we place non-trivial code inside these two methods, we can sensibly deteriorate the responsiveness of our pages.

As we will see in the next chapter, class Component provides method onConfigure which is more suited to contain code that contributes to determinate component states because it is called just once during rendering phase of a request.

6.2. Modifing tag attributes

To modify tag attributes in a component’s HTML markup we can use class org.apache.wicket.AttributeModifier. This class extends org.apache.wicket.behavior.Behavior and can be added to any component via the Component‘s add method. Class Behavior is used to expand component functionalities and it can also modify component markup. We will see this class in detail later in chapter 19.1.

As first example of attribute manipulation let’s consider a Label component bound to the following markup:

<span wicket:id="simpleLabel"></span>

Suppose we want to add some style to label content making it red and bolded. We can add to the label an AttributeModifier which creates the tag attribute style with value color:red;font-weight:bold:

label.add(new AttributeModifier("style", "color:red;font-weight:bold"));

If attribute style already exists in the original markup, it will be replaced with the value specified by AttributeModifier. If we don’t want to overwrite the existing value of an attribute we can use subclass AttributeAppender which will append its value to the existing one:

label.add(new AttributeAppender("style", "color:red;font-weight:bold"));

We can also create attribute modifiers using factory methods provided by class AttributeModifier and it’s also possible to prepend a given value to an existing attribute:

//replaces existing value with the given one
label.add(AttributeModifier.replace("style", "color:red;font-weight:bold"));

//appends the given value to the existing one
label.add(AttributeModifier.append("style", "color:red;font-weight:bold"));

//prepends the given value to the existing one
label.add(AttributeModifier.prepend("style", "color:red;font-weight:bold"));

6.3. Generating tag attribute ‘id’

Tag attribute id plays a crucial role in web development as it allows JavaScript to identify a DOM element. That’s why class Component provides two dedicated methods to set this attribute. With method setOutputMarkupId(boolean output) we can decide if the id attribute will be rendered or not in the final markup (by default is not rendered). The value of this attribute will be automatically generated by Wicket and it will be unique for the entire page. If we need to specify this value by hand, we can use method setMarkupId(String id). The value of the id can be retrieved with method getMarkupId().

Wicket generates markup ids using an instance of interface org.apache.wicket.IMarkupIdGenerator. The default implementation is org.apache.wicket.DefaultMarkupIdGenerator and it uses a session-scoped counter to generate the final id. A different generator can be set with the markup settings class org.apache.wicket.settings.MarkupSettings available in the application class:

@Override
public void init()
{
        super.init();
        getMarkupSettings().setMarkupIdGenerator(myGenerator);
}

6.4. Creating in-line panels with WebMarkupContainer

Creating custom panels is a great way to handle complex user interfaces. However, sometimes we may need to create a panel which is used only by a specific page and only for a specific task.

In situations like these org.apache.wicket.markup.html.WebMarkupContainer component is better suited than custom panels because it can be directly attached to a tag in the parent markup without needing a corresponding html file (hence it is less reusable). Let’s consider for example the main page of a mail service where users can see a list of received mails. Suppose that this page shows a notification box where user can see if new messages have arrived. This box must be hidden if there are no messages to display and it would be nice if we could handle it as if it was a Wicket component.

Suppose also that this information box is a

tag like this inside the page:

<div wicket:id="informationBox">
    //here's the body
    You've got <span wicket:id="messagesNumber"></span> new messages.
</div>

Under those conditions we can consider using a WebMarkupContainer component rather than implementing a new panel. The code needed to handle the information box inside the page could be the following:

//Page initialization code
WebMarkupContainer informationBox = new WebMarkupContainer ("informationBox");
informationBox.add(new Label("messagesNumber", messagesNumber));
add(informationBox);

//If there are no new messages, hide informationBox
informationBox.setVisible(false);

As you can see in the snippet above we can handle our information box from Java code as we do with any other Wicket component.

Note also that we may later choose to make information box visible by calling setVisible(true), upon for example an AJAX request (we will be covering such an example in chapter 19.2.8).

6.5. Working with markup fragments

Another circumstance in which we may prefer to avoid the creation of custom panels is when we want to conditionally display small fragments of markup in a page. In this case if we decided to use panels, we would end up having a huge number of small panel classes with their related markup file.

To better cope with situations like this, Wicket defines component Fragment in package org.apache.wicket.markup.html.panel. Just like its parent component WebMarkupContainer, Fragment doesn’t have its own markup file but it uses a markup fragment defined in the markup file of its parent container, which can be a page or a panel. The fragment must be delimited with tag and must be identified by a wicket:id attribute. In addition to the component id, Fragment‘s constructor takes as input also the id of the fragment and a reference to its container.

In the following example we have defined a fragment in a page and we used it as content area:

Page markup:

<html>
  ...
<body>
...
    <div wicket:id="contentArea"></div>
    <wicket:fragment wicket:id="fragmentId">
       <p>News available</p>
    </wicket:fragment>
</body>
</html>

Java code:

Fragment fragment = new  Fragment ("contentArea", "fragmentId", this);
add(fragment);

When the page is rendered, markup inside the fragment will be inserted inside div element:

<html>
  ...
<body>
...
    <div wicket:id="contentArea">
        <p>News available</p>
    </div>
</body>
</html>

Fragments can be very helpful with complex pages or components. For example let’s say that we have a page where users can register to our forum. This page should first display a form where user must insert his/her personal data (name, username, password, email and so on), then, once the user has submitted the form, the page should display a message like “Your registration is complete! Please check your mail to activate your user profile.”.

Instead of displaying this message with a new component or in a new page, we can define two fragments: one for the initial form and one to display the confirmation message. The second fragment will replace the first one after the form has been submitted:

Page markup:

<html>
<body>
        <div wicket:id="contentArea"></div>
        <wicket:fragment wicket:id="formFrag">
           <!-- Form markup goes here -->
        </wicket:fragment>
        <wicket:fragment wicket:id="messageFrag">
           <!-- Message markup goes here -->
        </wicket:fragment>
</body>
</html>

Java code:

Fragment fragment = new  Fragment ("contentArea", "formFrag", this);
add(fragment);

//form has been submitted
Fragment fragment = new  Fragment ("contentArea", "messageFrag", this);
replace(fragment);

6.6. Adding header contents to the final page

Panel’s markup can also contain HTML tags which must go inside header section of the final page, like tags