(Quick Reference)

6 Keeping control over HTML - Reference Documentation

Authors: Andrea Del Bene, Martin Grigorov, Carsten Hufe, Christian Kroemer, Daniel Bartl, Paul Borș

Version: 6.x

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 <span>) 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.

6.2 Modifing tag attributes

To modify tag attributes 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 17.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().

6.4 Creating in-line panels with WebMarkupContainer

Create 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 component org.apache.wicket.markup.html.WebMarkupContainer 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 <div> 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.

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 in a page small fragments of markup. 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 <wicket:fragment> 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">
	   <!-- Fragment markup goes here -->
	</wicket:fragment>
</body>
</html>

Java code:

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

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 <script> or <style>. To tell Wicket to put these tags inside page <head>, we must surround them with the <wicket:head> tag.

Considering the markup of a generic panel, we can use <wicket:head> tag in this way:

<wicket:head>
	<script type="text/javascript">
	  	function myPanelFunction(){
	  	}
	  </script>

<style> .myPanelClass{ font-weight: bold; color: red; } </style> </wicket:head> <body> <wicket:panel>

</wicket:panel> </body>

Wicket will take care of placing the content of <wicket:head> inside the <head> tag of the final page.

The <wicket:head> tag can also be used with children pages/panels which extend parent markup using tag <wicket:extend>.

The content of the <wicket:head> tag is added to the header section once per component class. In other words, if we add multiple instances of the same panel to a page, the <head> tag will be populated just once with the content of <wicket:head>.

The <wicket:head> tag is ideal if we want to define small in-line blocks of CSS or JavaScript. However Wicket provides also a more sophisticated technique to let components contribute to header section with in-line blocks and resource files like CSS or JavaScript files. We will see this technique later in chapter 15.

6.7 Using stub markup in our pages/panels

Wicket's <wicket:remove> tag can be very useful when our web designer needs to show us how a page or a panel should look like. The markup inside this tag will be stripped out in the final page, so it's the ideal place for web designers to put their stub markup:

<html>
<head>

</head> <body> <wicket:remove> <!-- Stub markup goes here --> </wicket:remove> </body> </html>

6.8 How to render component body only

When we bind a component to its corresponding tag we can choose to get rid of this outer tag in the final markup. If we call method setRenderBodyOnly(true) on a component Wicket will remove the surrounding tag.

For example given the following markup and code:

HTML markup:

<html>
<head>
  <title>Hello world page</title>
</head>
<body>
<div wicket:id="helloWorld">[helloWorld]</div>
</body>
</html>

Java code:

Label label = new Label("helloWorld", “Hello World!”);
label.setRenderBodyOnly(true);
add(label);

the output will be:

<html>
<head>
  <title>Hello world page</title>
</head>
<body>
 Hello World!
</body>
</html>

As you can see the <div> tag used for component Label is not present in the final markup.

6.9 Hiding decorating elements with the wicket:enclosure tag

Our data are rarely displayed alone without a caption or other graphic elements that make clear the meaning of their value. For example:

<label>Total amount: </label><span wicket:id="totalAmount"></span>

Wicket comes with a nice utility tag called <wicket:enclosure> that automatically hides those decorating elements if the related data value is not visible. All we have to do is to put the involved markup inside this tag. Applying <wicket:enclosure> to the previous example we get the following markup:

<wicket:enclosure> 
    <label>Total amount: </label><span wicket:id="totalAmount"></span>
</wicket:enclosure>

Now if component totalAmount is not visible, its description (Total amount:) will be automatically hidden. If we have more than a Wicket component inside <wicket:enclosure> we can use child attribute to specify which component will control the overall visibility:

<wicket:enclosure child="totalAmount"> 
    <label>Total amount: </label><span wicket:id="totalAmount"></span><br/>
	<label>Expected delivery date: </label><span wicket:id="delivDate"></span>
</wicket:enclosure>

child attribute supports also nested components with a colon-separated path:

<wicket:enclosure child="totalAmountContainer:totalAmount"> 
    <div wicket:id="totalAmountContainer">
		<label>Total amount: </label><span wicket:id="totalAmount"></span>
    </div>
    <label>Expected delivery date: </label><span wicket:id="delivDate"></span>
</wicket:enclosure>

6.10 Surrounding existing markup with Border

Component org.apache.wicket.markup.html.border.Border is a special purpose container created to enclose its tag body with its related markup. Just like panels and pages, borders also have their own markup file which is defined following the same rules seen for panels and pages. In this file <wicket:border> tag is used to indicate which part of the content is to be considered as border markup:

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<head></head>
<body>
    <!--  everything above <wicket:border> tag will be discarded...-->
    <wicket:border>
	 <div>
	    foo<br />
    <wicket:body/><br />
           buz <br />

</div> </wicket:border> <!-- everything below </wicket:border> tag will be discarded...--> </body> </html>

The <wicket:body/> tag used in the example above is used to indicate where the body of the tag will be placed inside border markup. Now if we attached this border to the following tag

<span wicket:id="myBorder">
  bar
</span>

we would obtain the following resulting HTML:

<span wicket:id="myBorder">
	<div>
  		foo<br />
  		bar<br />
  		buz <br />
	</div>
</span>

Border can also contain children components which can be placed either inside its markup file or inside its corresponding HTML tag. In the first case children must be added to the border component with method addToBorder(Component...), while in the second case we must use the add(Component...) method.

The following example illustrates both use cases:

Border class:

public class MyBorder extends Border {

public MyBorder(String id) { super(id); }

}

Border Markup:

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<head></head>
<body>
    <wicket:border>
	 <div>
	    <div wicket:id="childMarkup"></div>
    <wicket:body/><br />
         </div>
    </wicket:border>
</body>
</html>

Border tag:

<div wicket:id="myBorder">
  <span wicket:id="childTag"></span>
</div>

Initialization code for border:

MyBorder myBorder = new MyBorder("myBorder");

myBorder.addToBorder(new Label("childMarkup", "Child inside markup.")); myBorder.add(new Label("childTag", "Child inside tag."));

add(myBorder);

6.11 Summary

In this chapter we have seen the tools provided by Wicket to gain complete control over the generated HTML. However we didn't see yet how we can repeat a portion of HTML with Wicket. With classic server-side technologies like PHP or JSP we use loops (like while or for) inside our pages to achieve this result. To perform this task Wicket provides a special-purpose family of components called repeaters and designed to repeat their markup body to display a set of items.

But to fully understand how these components work, we must first learn more of Wicket's basics. That's why repeaters will be introduced later in chapter 13.