5 Keeping control over HTML - Reference Documentation
Authors: Andrea Del Bene, Carsten Hufe, Christian Kroemer, Daniel Bartl
Version: 1.0.0.BUILD-SNAPSHOT
Table of Contents
5 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.5.1 Hiding or disabling a component
At the end of the previous chapter we have seen how to hide a component calling its methodsetVisible
. 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.
5.2 Modifing tag attributes
To modify tag attributes we can use classorg.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 16.1.As first example of attribute manipulation let's consider a Label
component bound to the following markup:<span wicket:id="simpleLabel"></span>
AttributeModifier
which creates the tag attribute style
with value "color:red;font-weight:bold"
:label.add(new AttributeModifier("style", "color:red;font-weight:bold"));
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"));
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"));
5.3 Generating tag attribute 'id'
Tag attributeid
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()
.
5.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 componentorg.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>
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);
5.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 componentFragment
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>
Fragment fragment = new Fragment ("contentArea", "fragmentId", this); add(fragment);
<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>
Fragment fragment = new Fragment ("contentArea", "formFrag", this); add(fragment);//form has been submitted Fragment fragment = new Fragment ("contentArea", "messageFrag", this); replace(fragment);
5.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:{html}
<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>
{html}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 14.
5.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>
5.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 methodsetRenderBodyOnly(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>
Label label = new Label("helloWorld", “Hello World!”); label.setRenderBodyOnly(true); add(label);
<html> <head> <title>Hello world page</title> </head> <body> Hello World! </body> </html>
<div>
tag used for component Label
is not present in the final markup.
5.9 Hiding decorating elements with the 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: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>
totalAmount
is not visible, its description (Total amount:
) will be automatically hidden. If we have more then 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>
5.10 Surrounding existing markup with Border
Componentorg.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>
<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>
<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); }}
<?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>
<div wicket:id="myBorder"> <span wicket:id="childTag"></span> </div>
MyBorder myBorder = new MyBorder("myBorder");myBorder.addToBorder(new Label("childMarkup", "Child inside markup.")); myBorder.add(new Label("childTag", "Child inside tag."));add(myBorder);
5.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 (likewhile
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 12.