Wicket Introduction
Wicket Introduction
What is Wicket?
●
Wicket is a web framework. More precisely is a component-oriented
framework.
●
Component oriented frameworks difer from classic web
frameworks by building a model of requested page on the server
side and generating the HTML to send back according to this
model. You can think of the model as if it was an “inverse”
JavaScript DOM, meaning that:
1) is built on server-side
2) is built before HTML is sent to client
3) HTML code is generated using this model and not vice versa.
TextField component
●
Text felds elements are handled in Wicket with class TextField
●
Example for a TextField:
Java
new TextField<String>(“username”,Model<String>.of(“Insert username”));
HTML
Username: <input type="text" wicket:id="username"/>
●
Now we gonna look at a classic example of form to sign in a user
with a username and password.
* The example code for the two versions of the form is available on GitHub under module
LoginForm and LoginFormRevisited.
Component oriented framework
●
With this kind of framework our web pages and their HTML components
(forms, input controls, links, etc...), are pure class instances. Since
pages are class instances they live inside the JVM heap and we can
handle them as we do with any other Java class.
●
This approach is very similar to what GUI frameworks (like Swing or
SWT) do with desktop windows and their components. Wicket and the
other component oriented frameworks bring to web development the
same kind of abstraction that GUI frameworks ofer when we build a
desktop application.
●
This kind of framework hides the details of the HTTP protocol and
naturally solves the problem of its stateless nature.
Component oriented framework
●
Wicket allows us to design our web pages in terms of components and
containers, just like AWT does with desktop windows. Both frameworks
share the same component-based architecture: in AWT we have a
Windows instance which represents the physical windows containing GUI
components (like text felds, radio buttons, drawing areas, etc...), in
Wicket we have a WebPage instance which represents the physical web
page containing HTML components (pictures, buttons, forms, etc… ) .
Where is the HTML?
●
By default this HTML fle must have the same name of the related
page class and must be in the same package:
●
In Wicket we can use page classpath to put any kind of resource,
not just HTML (pictures, properties fle, etc...)
The web.xml file
●
A Wicket application is a standard Java EE web application, hence it
is deployed through a web.xml fle placed inside folder WEB-INF:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Wicket HelloWorld</display-name>
<filter>
<filter-name>WizardApplication</filter-name>
<filter-class>
org.apache.wicket.protocol.http.WicketFilter
</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>helloWorld.WicketApplication</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>WizardApplication</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Class WicketApplication
●
If we look back at web.xml we can see that we have provided the Wicket
flter with a parameter called applicationClassName. This subclass
represents our web application built with Wicket and it's responsible for
confguring it when the server is starting up.
●
Class Application comes with a set of confguration methods that we can
override to customize our application's settings. One of these methods is
getHomePage() that must be overridden as it is declared abstract:
package helloWorld;
import org.apache.wicket.Page;
import org.apache.wicket.protocol.http.WebApplication;
@Override
public Class<? extends Page> getHomePage() {
return HomePage.class;
@Override
Public init(){...}
}
HelloWorld page
●
To map Wicket components to HTML tags we must use attribute wicket:id.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1 wicket:id="label"></h1>
</body>
</html> HelloWorld.html
●
In the Wicket page we must add a component with the id equal to the value of the wicket:id
previously set:
package helloWorld;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
...
<body>
●
Inside <h1> tag you will fnd <h1 wicket:id="label">
</h1>
the value expressed as </body>
second parameter for Label’s ...
...
constructor. public HomePage(){
super();
add(new Label("label",
"Hello World"));
}
...
Wicket links
●
In HTML a link is basically a pointer to another resource that most of the time
is another page. Wicket implements links with component
org.apache.wicket.markup.html.link.Link, but it's more like onClick event
listener than a link:
}
});
}
}
Wicket links
●
By default after onClick has been executed, Wicket will send back to the
current page to the client web browser. If we want to navigate to another
page we must use method setResponsePage of class Component:
●
The HTML outside tag <wicket:panel> will be removed during rendering phase. The
space outside this tag can be used by both web developers and web designers to
place some mock HTML to show how the fnal panel should look like.
JavaScript and CSS in panels
●
If we want to add header resources to a panel (for example a css), we can use
tag <wicket:head>. It’s content will be included in the <head> tag of the page.
NOTE: resources are added once per panel class to avoid duplicates.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8">
<title>Insert title here</title>
<wicket:head>
<script>...</script>
<wicket:head>
...
</head>
<body>
...
<wicket:panel>
…
</wicket:panel>
...
Models and forms
Model in Wicket
●
A model is a facade for the data object that a component must display
(for example with a label) or set (for example with a textfeld).
●
Every component has a related model that can be accessed with method
getModel(). A model can be shared among diferent components.
●
Up to Wicket 7, a model was a simple interface that just defned a
method to read an object and another one to write it:
public interface IModel
{
public Object getObject();
public void setObject(final Object object);
}
●
With models our components can handle data without knowing
how they are physically persisted or retrieved.
Model in Wicket
●
Also Label has a model, but it's “hidden” inside the component and
contains the second parameter of the constructor:
add(new Label("label", "Hello World"));
...
●
Wicket comes with a set of models suited for diferent needs. The most
basic one is class Model. We can wrap any object in this model and we can
use factory method of to avoid explicit instantiations:
new Model<String>("label");
…
Model<Persona>.of(new Person(“Mario”, “Rossi”));
…
●
Every component has a set of method to access its model and the
object inside it:
Model in Wicket 8
●
With Wicket 8 model interface has changed to take advantage of the
new syntax features introduced in Java 8.
●
In short, IModel has become a functional interface and provides a
default empty implementation for setObject:
public interface IModel
{
public Object getObject();
default void setObject(final T object) {
throw new UnsupportedOperationException(
"Override this method to support setObject(Object)");
}
}
●
In this way models are read-only by default, and can be implemented
with lambda expressions:
add(new Label("timeStamp", () -> LocalDate.now()));
Models and forms
●
In Wicket the concept of model is probably the most important topic of
the entire framework and it is strictly related to the usage of its
components.
●
In addition, models are also an important element for localization
support (see user guide for more details).
●
However, despite their fundamental role, models are not difcult to
understand but the best way to get acquainted with them is to use
them with forms.
●
Hence, to continue our introduction to Wicket models, in the next
slides we will introduce Wicket forms a very basic form component,
the TextField.
Forms in Wicket
●
Following its component oriented nature, Wicket ofers a Form
component to handle HTML forms.
●
Forms are special container for input components (representing text
felds, radio buttons, check boxes, etc...) which are subclasses of
org.apache.wicket.markup.html.form.FormComponent.
●
Class Form comes with a callback method onSubmit which is invoked
when form is submitted.
●
Now we gonna look at a classic example of form to sign in a user
with a username and password.
* The example code for the two versions of the form is available on GitHub under module
LoginForm and LoginFormRevisited.
Example form: the markup
<html>
…
<div style="margin: auto; width: 40%" class=""> 4 components: a form, a
<form id="search" method="get" wicket:id="form"> label, a text field and a
<fieldset class="center">
<legend >Search</legend>
password field.
<p wicket:id="loginStatus" style=""></p>
<span>Username: </span>
<input wicket:id="username" type="text" id="username" />
<br/>
<span >Password: </span>
<input wicket:id="password" type="password" id="password" />
<p>
<input type="submit" name="login" value="login"/>
</p>
</fieldset>
</form> Wicket comes with a specific component for password
</div> fields.
…
</html>
Example form: first version
public class LoginForm extends Form {
private TextField usernameField;
private PasswordTextField passwordField;
private Label loginStatus;
add(new TextField("username"));
add(new PasswordTextField("password")); Components ids and fields names
}
add(new Label("loginStatus")); are the same.
public final void onSubmit() {
if(username.equals("test") && password.equals("test"))
loginStatus = "Congratulations!";
else Model has become completely
loginStatus = "Wrong username or password !"; transparent to the developer.
}
}
More about forms and models
●
Forms are a wide-ranging topic and can not be fully covered with
this presentation.
●
There’s also much more to say about models, especially if you use
them in Wicket 8 with lambda support.
●
For a full coverage of forms and models see the user guide:
https://wicket.apache.org/learn/#guide
Resource handling
Resource handling
●
With “resource” we indicate both static resources (such as JS and CSS
fles) and dynamic resources (those who returns they value on the fly
[ex: a RSS]).
●
From a technical point of view, in Wicket a resource is just an
implementation of interface org.apache.wicket.request.
resource.IResource.
●
Working with dynamic resources is less frequent in Wicket and it
implies a custom implementation of IResource.
●
On the contrary handling static resources is a very common task. With
Wicket we can specify not just the static resource to load, but also its
dependencies and its loading priority.
Static resources
●
In general static resources are loaded from 3 possible sources:
- A generic fle from local flesystem
- A fle from classpath (via ClassLoader)
- An URL
●
In Wicket resources are instantiated using a reference to them rather
than directly. In this way they can be lazy-loaded the frst time they
are requested.
●
Resource references are instances of class org.apache.wicket.
request.resource.ResourceReference.
●
Most of the time static resources are JS or CSS fles which can be
referred to as header items.
Header items
●
As the name suggests, an header item is simply an element that is
placed inside the <head> tag of the page. In Wicket header items are
usually built from a resource reference and are instances of class
org.apache.wicket.markup.head.HeaderItem (for example
JavaScriptHeaderItem)
●
A page or one of its component can add an header item overriding
its method renderHead:
Resources
●
CssHeaderItem: for CSS content.
●
JavaScriptHeaderItem: for JavaScript content.
●
StringHeaderItem: render free text in the header section.
●
As we said before, we can declare dependencies on header items and resources:
Url jqueyuiUrl = Url.parse("https://ajax.googleapis.com/ajax/libs/jqueryui/" +
"1.10.2/jquery-ui.min.js");
UrlResourceReference jqueryuiRef = new UrlResourceReference(jqueyuiUrl){
@Override
public List<HeaderItem> getDependencies() {
Application application = Application.get();
ResourceReference jqueryRef = …;
return Arrays.asList(JavaScriptHeaderItem.forReference(jqueryRef));
}
};
JavaScriptReferenceHeaderItem javaScriptHeaderItem =
JavaScriptHeaderItem.forReference(jqueryuiRef);
Priority Header Item
●
PriorityHeaderItem: wraps another header item and ensures
that it will have the priority over the other items.
There are also header items meant to work with JavaScript events. In this
way we can execute our code only when a specifc event occurs.
●
OnDomReadyHeaderItem: JavaScript code that will be executed after
the DOM has been built, but before external fles will be loaded.
OnDomReadyHeaderItem item = new OnDomReadyHeaderItem(";alert('hello!');");
●
OnLoadHeaderItem: execute JavaScript code after the whole page is
loaded.
OnLoadHeaderItem item = new OnLoadHeaderItem(";alert('hello!');");
●
OnEventHeaderItem: execute JavaScript code when a specifc event is
triggered.
OnEventHeaderItem item = new OnEventHeaderItem("elementId",
"eventName", ";alert('Hello!');");
Bundle resources
Now, when one of the resources included in the bundle is requested, the entire
bundle is served, i.e. the page will contain the JavaScript entry plugins-bundle.js
, which includes all the bundle resources.
AJAX support
“Transparent” AJAX
●
Now we can “ajaxify” components adding AJAX behaviors. In Wicket a
behaviors are quite like plug-ins that can enrich a component with
new features.
●
For example org.apache.wicket.ajax.AjaxEventBehavior provides the
means to handle an event on server side via AJAX:
Java Code HTML Template
●
AjaxRequestTarget can be used also to enrich AJAX response with
JavaScript code and header items:
protected void onEvent(AjaxRequestTarget
target) {
target.appendJavaScript(";alert('hello!');");
target.getHeaderResponse().render(headerItem);
}
Built-in AJAX components and
behaviors
●
Java 8 lambdas are quite suited for writing callback code, which is
what we do to handle AJAX events.
●
With Wicket 8 an AJAX click handler can be written leveraging lambdas
in the following way:
AjaxEventBehavior.onEvent("click", target -> target.add(component));
Testing with Wicket
Test in isolation
●
Test Driven Development (and unit testing) has become a fundamental
activity in our everyday-job. Wicket ofers a rich set of helper classes
that allows us to test our applications in isolation using just JUnit.
●
With “just JUnit” we mean:
1) We don’t need to have a running server
2) We don’t need to run tests for a specifc browser (like we do with
Karma)
3) No additional library required, just Wicket and JUnit (no need of
browser automation tools like Selenium)
Test in isolation
●
The central class in a Wicket testing is org.apache.wicket.util.tester.WicketTester.
This utility class provides a set of methods to render a component, click links,
check page content, etc...
public class TestHomePage {
private WicketTester tester;
@Before
public void setUp() {
tester = new WicketTester(new WicketApplication());
}
@Test
public void testHomePageLink() {
//start and render the test page
tester.startPage(HomePage.class);
//assert rendered page class
tester.assertRenderedPage(HomePage.class);
//move to an application link
tester.executeUrl("./foo/bar");
//test expected page for link
tester.assertRenderedPage(AnotherHomePage.class);
}
}
Testing the response
●
WicketTester allows us to access to the last response generated during
testing with method getLastResponse. Utility class Mock-
HttpServletResponse is returned to extract informations from mocked
request.
●
Resulting markup can be tested at tag-level with TagTester:
Testing the response
Response content:
<html xmlns:wicket="http://wicket.apache.org">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<span class="myClass"></span>
<div class="myClass"></div>
</body>
</html>
JUnit code:
String responseContent = tester.getLastResponse().getDocument();
//look for a tag with 'class="myClass"'
TagTester tagTester = TagTester.createTagByAttribute(responseTxt,
"class", "myClass");
assertEquals("span", tagTester.getName());
List<TagTester> tagTesterList = TagTester.createTagsByAttribute(responseTxt,
"class", "myClass", false);
assertEquals(2, tagTesterList.size());
Testing AJAX events
●
AJAX components can be tested as well “triggering” the JavaScript
event they handle:
Page Code
Label label = new Label("label", "Hello World!");
Label otherLabel = new Label("otherLabel", "hola!");
label.setOutputMarkupId(true);
label.add(new AjaxEventBehavior("click") {
@Override
protected void onEvent(AjaxRequestTarget target) {
target.add(otherLabel);
}
});
Test Code
label.add(new AjaxEventBehavior("click") {
@Override
protected void onEvent(AjaxRequestTarget target) {
target.add(otherLabel);
}
});
Test Code
●
AJAX behaviors can also be tested in isolation, relying only on
WicketTester:
Test Code
AjaxFormComponentUpdatingBehavior ajaxBehavior =
new AjaxFormComponentUpdatingBehavior("change"){
@Override
protected void onUpdate(AjaxRequestTarget target) {
//...
}
};
component.add(ajaxBehavior);