X

Geertjan's Blog

  • April 9, 2006

Panels and Borders in Wicket and NetBeans

Geertjan Wielenga
Product Manager
The problem with Wicket's Hello World! application is that it should be followed up by a simple quick start that extends the "Hello World!" scenario with two of Wicket's key concepts, both of which are initially quite tricky: Border and Panel. It's a good idea to understand both these concepts right from the start, i.e., right after "Hello World", because they powerfully illustrate two of Wicket's main driving forces: component reuse and consistency in look and feel. Panels deal with component reuse, while borders deal with consistent look-and-feel of individual pages within a website.

So, in NetBeans (assuming you have the modules that I'm working on), you'd first create an application that has a lot of Wicket-specific files and code pre-generated for you. In the second panel of the "New Project wizard", you'd specify Wicket-specific details (below, you see all the defaults, i.e., I didn't type in anything myself):

Asuming you take all the defaults, your starting point in NetBeans is the following:

Next, before going further, let's create a panel and a border. In the New File wizard, we have a template for each:

For both the panel and the border, we get two files: an HTML file and a Java file. Both are quite rudimentary, but provide quite a bit of info already. For example, on the Java side, the correct super class is extended and import statements are included. (In the New File wizard, there is a drop-down list where you can either select the default superclass or where an alternative can be typed manually.) On the HTML side, the tags that often cause a lot of confusion are already there, i.e., the <wicket:panel> and <wicket:border> tags. In both cases, there's already some preliminary code provided. For example, for the panel, this is what you get:

<html>
<head>
<title></title>
</head>
<body>
<wicket:panel>
<span wicket:id="message">this text will be replaced</span>
</wicket:panel>
</body>
</html>

And you get this on the Java side:

package com.myapp.wicket;
import wicket.markup.html.panel.Panel;
import wicket.markup.html.basic.Label;
public class NewPanel extends Panel {
/\*\* Creates a new instance of NewPanel \*/
public NewPanel(String id) {
super(id);
add(new Label("message", "I am a reusable component!"));
}
}

There's a similar structure for the border component. On the HTML side, there's a lot more, because the border captures the entire body of whatever component it works with. This body border is based on Wicket's NavoMatic sample, excluding the navigation border and navigation links.

So, I haven't typed anything yet, I haven't touched my keyboard at all, and already I have a full source structure for the basis of my application (as well as, by the way, entries in web.xml, so that I will not even need to look at this file at all ever again). Now, the scenario for after "Hello World" begins, assuming that you've gone through the New Project wizard and added a panel and a border afterwards, from the New File wizard. The trickiest concept to understand is that, in Wicket, one adds components to borders, and not the other way round. So, let's begin with a quick start on panels and borders...

  1. In NewPanel.java, make the panel's label component a child of the border component by adding the label component to the border component, as follows (the only changed part in the generated NewPanel.java is highlighted below):

    public NewPanel(String id) {
    super(id);add(new NewBodyBorder("panelBorder")
    .add(new Label("message", "I am a reusable component!")));

    }

  2. In NewPanel.html, add the border to the panel's markup (the only changes to the generated NewPanel.html are highlighted below):

    <html>
    <head>
    <title></title>
    </head>
    <body>
    <wicket:panel><span wicket:id="panelBorder">
    <span wicket:id="message">this text will be replaced</span></span>
    </wicket:panel>
    </body>
    </html>

  3. Your panel is a reusable component that now includes a border. Take a look at NewBodyBorder.java and NewBodyBorder.html to see what the the border will do for your panel. In NewBodyBorder.java, you see that a BoxBorder will be created, which draws a thin black line around its children.

  4. Now let's add the panel to our Homepage. Note that we can add it to as many pages as we want, as many times as we need. Since we only have one web page, we'll add it there. In Home.java add the following line to the Constructor:

    add(new NewPanel("newPanel"));

    In Home.html add this line between the body tags:

    <span wicket:id="newPanel">this text will be replaced</span>

  5. Now, when you deploy the application, you will see the following in your browser:

Remember: you can add your panel component to any web page you like, in the same way that you added it to the Homepage. You can also reuse the border over and over again. In fact, that's the point: continue using the border if you want your panels (and/or pages) to have the same look and feel. And, whenever you want to use a border, around a panel or a page, you need to add the border's children to the border, and not the other way round.

Join the discussion

Comments ( 3 )
  • Geertjan Monday, April 10, 2006
    I realized later that the panel could also have been added to the border itself... That's pretty cool too.
  • Gaxton Okobah Friday, December 22, 2006
    Hi! I like ur tutorials... I'm new to wickets and I experienced a minor while writing a wicket program with Netbeans 5.5..
    hold on,
    When I ran the project, this is the error:
    WicketMessage:
    "Method onLinkClicked of interface wicket.markup.html.link.ILinkListener targeted at component [MarkupContainer [Component id = panel, page = com.sample.HomePage, path = 0:panel.HomePage$1, isVisible = true, isVersioned = true]] threw an exception"
    These are the java codes:
    /\*
    \* EducationData.java
    \*
    \* Created on December 11, 2006, 12:00 PM
    \*
    \* To change this template, choose Tools | Template Manager
    \* and open the template in the editor.
    \*/
    package com.nairanet.nelx.forms;
    import com.nairanet.nelx.lists.ChoiceLists;
    import java.io.Serializable;
    import java.util.Date;
    import wicket.AttributeModifier;
    import wicket.ajax.AbstractAjaxTimerBehavior;
    import wicket.ajax.AjaxRequestTarget;
    import wicket.markup.html.WebMarkupContainer;
    import wicket.markup.html.form.Form;
    import wicket.ajax.markup.html.form.AjaxSubmitButton;
    import wicket.markup.html.form.DropDownChoice;
    import wicket.markup.html.form.TextField;
    import wicket.markup.html.panel.FeedbackPanel;
    import wicket.markup.html.panel.Panel;
    import wicket.util.time.Duration;
    import wicket.model.CompoundPropertyModel;
    import wicket.model.Model;
    import wicket.markup.html.basic.Label;
    /\*\*
    \*
    \* @author Gaxton
    \*/
    public class EducationData extends Panel{
    //The following are The textfield that holds the data that people will feed in
    private TextField universityAttended;
    private TextField yearofEntry;
    private TextField yearGraduated;
    private TextField awardReceived;
    private TextField grade;

    private EducationDataBean educationDataBean;

    /\*\*
    \* A special component that returns feedback messages to the user after submit button is clicked
    \* calls to info(), error() or warn() will pipe messages to this component HTML markup
    \*/
    private final FeedbackPanel feedback;
    /\*\*
    \* A Component that will be used to echo back what the user filled into the form
    \*each time they click on Submit Button
    \*/
    private final WebMarkupContainer summaryDetails;
    /\*\*
    \* Creates a new instance of EducationData
    \*/
    public EducationData(String id){
    super(id);
    setEducationDataBean(new EducationDataBean());
    final Form educationDataForm = new Form("educationDataFormSet", new CompoundPropertyModel(getEducationDataBean()));

    initComponents(educationDataForm);
    /\*\*
    \* This special component will take form feedback messages back to the user when required
    \* E.g. Can be used to inform the user that Data Sucessfully Saved
    \*/
    feedback = new FeedbackPanel("feedback");
    getFeedback().setOutputMarkupId(true);
    educationDataForm.add(getFeedback());
    /\*\*
    \* This is the instantiation of the Component that echos form field back to the user via the
    \*HTML markup with ID=summaryDetails.
    \* It is bound to bioDataBean instance. The BioDataBean instance is automatically filled with
    \* data from the Form which is then used by these component
    \*/
    summaryDetails = new WebMarkupContainer("summaryDetails",new CompoundPropertyModel(getEducationDataBean()));
    createSummaryDetails(educationDataForm);

    //...AjaxSubmitButton has a HTML markup within the HTML, this is enclosed in the last <tr> tags
    // in the .HTML
    AjaxSubmitButton saveData = new AjaxSubmitButton("saveData",educationDataForm){
    protected void onSubmit(AjaxRequestTarget ajax, Form form){
    System.out.println(getEducationDataBean());
    sendFeedbackMessage("Updated Information Summary", MESSAGE_TYPE.INFO);
    ajax.addComponent(getFeedback());
    getSummaryDetails().add(new AttributeModifier("style", true, new Model("display:block")));
    ajax.addComponent(getSummaryDetails());
    }
    };
    educationDataForm.add(saveData);
    add(educationDataForm);
    }
    private void initComponents(Form _educationDataForm){

    universityAttended = new TextField("universityAttended");
    universityAttended.setOutputMarkupId(true);
    _educationDataForm.add(universityAttended);
    yearofEntry = new TextField("yearofEntry");
    yearofEntry.setOutputMarkupId(true);
    _educationDataForm.add(yearofEntry);
    yearGraduated = new TextField("yearGraduated");
    yearGraduated.setOutputMarkupId(true);
    _educationDataForm.add(yearGraduated);

    awardReceived = new TextField("awardReceived");
    awardReceived.setOutputMarkupId(true);
    _educationDataForm.add(awardReceived);
    grade = new TextField("grade");
    grade.setOutputMarkupId(true);
    _educationDataForm.add(grade);
    }
    //This method is called when a Feedback message needs to be sent to the user of the form
    private void sendFeedbackMessage(String message,MESSAGE_TYPE type){
    switch(type){
    case ERROR:
    getFeedback().add(new AttributeModifier("bgcolor",true,new Model("#F28C8E")));
    error(message);
    break;
    case INFO:
    getFeedback().add(new AttributeModifier("bgcolor",true,new Model("#99CC00")));
    info(message);
    break;
    case WARNING:
    getFeedback().add(new AttributeModifier("bgcolor",true,new Model("#FFFF9")));
    warn(message);
    break;
    }
    }

    /\*\*
    \* An enum for messages. used by sendFeedback to decide wat color to use for background in
    \*all cases
    \*/
    private enum MESSAGE_TYPE {
    ERROR, INFO, WARNING
    }
    /\*\*
    \* Creation of all the corresponding component that is added inside the
    \* SummaryDetails Component
    \*/
    private void createSummaryDetails(Form _educationDataForm){
    getSummaryDetails().setOutputMarkupId(true);
    getSummaryDetails().add(new AttributeModifier("style", true, new Model("display:none;")));
    getSummaryDetails().add(new Label("universityAttended"));
    getSummaryDetails().add(new Label("yearofEntry"));
    getSummaryDetails().add(new Label("yearGraduated"));
    getSummaryDetails().add(new Label("awardReceived"));
    getSummaryDetails().add(new Label("grade"));
    _educationDataForm.add(getSummaryDetails());
    }
    private class EducationDataBean implements Serializable{
    private String universityAttended;
    private String yearofEntry;
    private String yearGraduated;
    private String awardReceived;
    private String grade;
    public EducationDataBean(){
    }
    public String getUniversityAttended(){
    return universityAttended;
    }
    public void setUniversityAttended(String universityAttended){
    this.universityAttended = universityAttended;
    }
    public String getYearofEntry(){
    return yearofEntry;
    }
    public void setYearofEntry(String yearofEntry){
    this.yearofEntry =yearofEntry;
    }

    public String getYearGraduated(){
    return yearGraduated;
    }
    public void setYearGraduated(String yearGraduated){
    this.yearGraduated = yearGraduated;
    }
    public String getAwardReceived(){
    return awardReceived;
    }
    public void setAwardReceived(String awardReceived){
    this.awardReceived = awardReceived;
    }
    public String getGrade(){
    return grade;
    }
    public void setGrade(String grade){
    this.grade = grade;
    }
    public String toString(){
    return "
    Form Representation:
    School or Institution: " + getUniversityAttended() +
    "
    Year of Entry :" + getYearofEntry() +
    "
    Year of Completion :" + getYearGraduated() +
    "
    Completion Award Types:" + getAwardReceived() +
    "
    Gading/Class:" + getGrade();

    }

    }

    private EducationDataBean getEducationDataBean() {
    return educationDataBean;
    }
    private void setEducationData(EducationDataBean educationDataBean) {
    this.educationDataBean = educationDataBean;
    }
    private FeedbackPanel getFeedback(){
    return feedback;
    }
    private WebMarkupContainer getSummaryDetails(){
    return summaryDetails;
    }
    private void setEducationDataBean(EducationDataBean educationDataBean) {
    }
    }
    //.....These is the Html code
    <wicket:panel>
    <form wicket:id ="educationDataForm">
    <table width="500" border="0">
    <tr>
    <td width="165">School or Institution</td>
    <td width="325"><span style="width:400px; height:25px; text-align:justify;">
    <input wicket:id="universityAttended" style=" background-image:url(images/dark_3d.gif)" name="textfield" type="text" class="textfieldShadeFill" size="50" />
    </span></td>
    </tr>
    <tr>
    <td>Year Of Entry </td>
    <td><span style="width:400px; height:25px;">
    <input wicket:id="yearofEntry" name="textfield23" type="text" class="textfieldShadeFill" style="background-image:url(images/dark_3d.gif)" value="1990" size="5" />
    </span></td>
    </tr>
    <tr>
    <td>Year Of Completion </td>
    <td><input wicket:id="yearGraduated"name="textfield22" type="text" class="textfieldShadeFill" style="background-image:url(images/dark_3d.gif)" value="2005" size="5" /></td>
    </tr>
    <tr>
    <td>Completion Awards Type </td>
    <td><span style="width:400px; height:25px;">
    <input wicket:id="awardReceived"name="textfield222" type="text" class="textfieldShadeFill" style="background-image:url(images/dark_3d.gif)" value="2005" size="25" />
    (e.g. SSCE. B.Sc.)</span></td>
    </tr>
    <tr>
    <td>Grading/Class </td>
    <td><span style="width:400px; height:25px;">
    <input wicket:id="grade"style="background-image:url(images/dark_3d.gif)" name="textfield2222" type="text" class="textfieldShadeFill" size="5" />
    </span></td>
    </tr>
    <tr>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
    </tr>
    <tr>
    <td colspan="2">Add Another School/Higher Institution </td>
    </tr>
    <tr>
    <td></td>
    <td >
    <span wicket:id="feedback"></span>
    </td>
    </tr>
    <tr>
    <td></td>
    <td >
    <!-- Markup Representation for WebMarkupContainer summaryDetails component-->
    <div style="width:450px;" wicket:id="summaryDetails">
    <div style="width: 125px; padding-bottom:4px;">School or Higher Institution (please verify)</div>
    <div style="width: 325px; background-color:#99CC00;padding-bottom:4px;">
    <span wicket:id="universityAttended"></span>
    </div>
    <div style="width: 125px;padding-bottom:4px;">Year of Entry</div>
    <div style="width: 325px;background-color:#99CC00;padding-bottom:4px;">
    <span wicket:id="yearofEntry"></span>
    </div>
    <div style="width: 125px;padding-bottom:4px;">Year of Completion</div>
    <div style="width: 325px;background-color:#99CC00;padding-bottom:4px;">
    <span wicket:id="yearGraduated"></span>
    </div>
    <div style="width: 125px;padding-bottom:4px;">Completion Award</div>
    <div style="width: 325px;background-color:#99CC00;padding-bottom:4px;">
    <span wicket:id="awardReceived"></span>
    </div>
    <div style="width: 125px;padding-bottom:4px;">Grading Or Class</div>
    <div style="width: 325px;background-color:#99CC00;padding-bottom:4px;">
    <span wicket:id="grade"></span>
    </div>
    </div>
    <!-- End of Markup Representation for WebMarkupContainer component -->
    </td>
    </tr>
    <tr>
    <td>To Update Your Profile Now, Click Here</td>
    <td><input wicket:id="saveData" type="submit" value="Save Data" /></td>
    </tr>
    </table>
    </form>
    </wicket:panel>
    Thank you...
  • Phil Willemann Friday, January 12, 2007
    Where are the netbeans modules? I have all the jars, but would love to have the modules so I could have code pre-generated.
    Do I need to check it out of CVS?
    Thanks
    Phil Willemann
    P.S. I have been looking for something like Wicket. It came along at the right time. Thanks
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.