Controls

Click provides a rich set of Controls which support client side rendering and server side processing. This section covers the following topics:

 

While this section provides an overview how Controls work please see the Javadoc, which provides extensive information and examples.

Control Interface

Controls provide the server side components that process user input, and render their display to the user. Controls are equivalent to Visual Basic Controls or Delphi Components.

Controls handle the processing of user input in the onProcess method and render their HTML display using the toString() method. The execution sequence for a Control being processed and rendered is illustrated below in Figure 1.

Figure 1.   Post Sequence Diagram - created with Enterprise Architect courtesy Sparx Systems

In Click all control classes must implement the Control interface. The Control interface is depicted below in Figure 2.

Figure 2.   Control Interface Diagram - created with Enterprise Architect courtesy Sparx Systems

Methods on the Control interface include:

Control Callback

Click Controls provide an event callback mechanism similar to a java.awt.ActionListener callback.

To define a control listener, simply set the listener object instance and the name of the listener method to be invoked. For example:

public class SimpleCallback extends Page {

    public SimpleCallback() {
        ActionLink clickLink = new ActionLink("clickLink");

        // Set the link's listener to callback this page's onClick method.
        clickLink.setListener(this, "onClick");
        addControl(clickLink);    
    }
    
    public boolean onClick() {
        System.out.println("onClick invoked");
        return true;
    }
} 
The listener method can have any name but it must have take no parameters and must return a boolean or java.lang.Boolean value.

When a callback method returns true the processing of other Controls will continue and the Pages onGet() or onPost() event handler will be called. If a callback method returns false no further Control processing will be performed and neither of the Page onGet() or onPost() methods will be invoked. This execution logic is illustrated in the Page Execution Activity Diagram.

Being able to stop further processing and do something else can be very handy. For example your Pages onGet() or onPost() method may perform an expensive database operation. By returning false in an event handler you can skip this step and render the template or forward to the next page.

Control Classes

Core control classes are defined in the package net.sf.click.control. This package includes controls for the essential HTML elements.

Extended control classes are provided in the Click Extras package net.sf.click.extras.control. Click Extras classes can contain dependencies to 3rd party frameworks.

A subset of these control classes are depicted below in Figure 3.

Figure 3.   Package Class Diagram - created with Enterprise Architect courtesy Sparx Systems

The key control classes include: The control classes are designed to support subclassing for customized behaviour. All control fields have protected visibility and have public accessor methods.

You can also aggregate controls to build more complex controls. For example the CreditCardField uses a Select control to render the different credit card types.

Message Properties

Control strings for field validation messages and HTML formatting strings are externalized in the properties file. By using these properties files you can localize a Click application for your particular language and dialect.

Message Resolution

Messages are looked up in a particular order enabling taylor specific messages, for your controls, individual pages or across your entire application. The order in which localized messages are resolved is:
Page scope messages
Message lookups are first resolved to the Page classes message bundle if it exists. For example a Login page may define the message properties:
 /com/mycorp/page/Login.properties 
If you want to tailor messages for a particular page this is where to place them.
Global page scope messages
Next message lookups are resolved to the global pages message bundle if it exists.
 /click-page.properties 
If you want messages to be used across your entire application this is where to place them.
Control scope messages
Next message lookups are resolved to the Control classes message bundle if it exists. For example a CustomTextField control may define the message properties:
 /com/mycorp/control/CustomTextField.properties 
Global control scope messages
Finally message lookups are resolved to the global application control message bundle if the message has not already been found. The global control properties file is:
 /click-control.properties 

Control Properties

To customize the click-control.properties simply add this file to your classpath and tailor the specific values.

Note when customizing the message properties you must include all the properties, not just the ones you want to override.

# Click Control messages
field-maxlength-error={0} must be no longer than {1} characers
field-minlength-error={0} must be at least {1} characters
field-required-error=You must enter a value for {0}

file-required-error=You must enter a filename for {0}

label-required-prefix=
label-required-suffix=<span class="required">*</span>
label-not-required-prefix=
label-not-required-suffix=&nbsp;

not-checked-error=You must select {0}

number-maxvalue-error={0} must not be larger than {1}
number-minvalue-error={0} must not be smaller than {1}

select-error=You must select a value for {0}

table-first-label=First
table-first-title=Go to first page
table-previous-label=Prev
table-previous-title=Go to previous page
table-next-label=Next
table-next-title=Go to next page
table-last-label=Last
table-last-title=Go to last page
table-goto-title=Go to page
table-page-banner=<span class="pagebanner">{0} items found, displaying {1} to {2}.</span>
table-page-banner-nolinks=
  <span class="pagebanner-nolinks">{0} items found, displaying {1} to {2}.</span>
table-page-links=<span class="pagelinks">[{0}/{1}] {2} [{3}/{4}]</span>
table-page-links-nobanner=<span class="pagelinks-nobanner">[{0}/{1}] {2} [{3}/{4}]</span>

# Message displayed when a error occurs when the application is in "production" mode
production-error-message=
  <div id='errorReport' class='errorReport'>The application encountered an unexpected error.
  </div> 

Accessing Messages

Field classes support a hierarchy of resource bundles for displaying validation error messages and display messages. These localized messages can be accessed through the Field methods: These methods use the Locale of the request to lookup the string resource bundle, and use MessageFormat for any string formatting.

Container

The concept of Container extends the Control interface, and enables construction of complex component hierarchies.

Container enables components to add, remove and retrieve other controls and containers.

The following Click Controls are example Containers:


These containers are depicted below in Figure 4.

Figure 4.   Containers Class Diagram

The following convenient classes is available to work with and create custom Containers: Lets cover each of them here.

AbstractContainer

Enables easy creation of custom Containers for example a html div or span element.

For example:

public class Div extends AbstractContainer {
    
    public Div(String name) {
        super(name);
    }

    public String getTag() {
        // Return the control's HTML tag.
        return "div";
    }
} 
Lets test the Div: (note the MockContext used in this test is described in the Mock Test Support documentation)
public class Test {
    public static void main (String args[]) {
        // Create mock context in which to test the container.
        MockContext.initContext();

        // Create a div instance called "mydiv"
        String containerName = "mydiv";
        Div mydiv = new Div(containerName);
        
        // Add a control to the container
        mydiv.add(new TextField("myfield"));

        System.out.println(mydiv);
    }
} 

Executing the above example results in the following output:

<div name="mydiv" id="mydiv">
    <input type="text" name="myfield" id="myfield" value="" size="20" />
</div> 

BasicForm

As mentioned earlier Form only allow Field and FieldSet controls to be added. Having this restriction enables the Form to layout its contents automatically and in a predictable way.

Form also provides automatic error handling, which when combined with auto-layout, allows you to quickly assemble input screens.

BasicForm on the other hand allows you to manually layout controls and errors. Any control can be added including tables, trees, panels and lists.

BasicForm provides everything you need to work with html forms, including the following helpful methods:


AbstractContainerField

AbstractContainerField extends Field and implements the Container interface. This provides a convenient base class for those cases where you need both a Field and Container.

Since Form only works with Fields, AbstractContainerField is useful when you want to adapt a container to a field. For example:

public class FieldContainer extends AbstractContainerField {

    public FieldContainer(String name) {
        super(name);
    }

    // Return the html tag to render
    public String getTag() {
        return "div";
    }
} 
FieldContainer can safely be used with Form, since FieldContainer is a field.

Please note that FieldContainer is just a normal container and any control can be added to it including tables, trees and panels. This circumvents the restriction placed on Form which can only accept fields as children. Its up to you to decide whether its a good idea to add non-field controls inside a Form. Normally it would be better to resort to BasicForm for these situations as you will have full control over the form layout.

To test the new class we use the following snippet:

public class Test {
    public static void main (String args[]) {
        // Create mock context in which to test the container.
        MockContext.initContext();

        // Create a FieldContainer instance called "field_container"
        String containerName = "field_container";
        FieldContainer fieldContainer = new FieldContainer(containerName);
        
        // Add a couple of fields to the container
        fieldContainer.add(new TextField("myfield"));
        fieldContainer.add(new TextArea("myarea"));

        System.out.println(fieldContainer);
    }
} 
Executing the snippet produces the output:
<div name="field_container" id="field_container">
    <input type="text" name="myfield" id="myfield" value="" size="20"/>
    <textarea name="myarea" id="myarea" rows="3" cols="20"></textarea>
</div> 

Layout

Controls such as Form take care of layout and error reporting automatically, and for many use cases this auto-layout is good enough.

However for custom or complex layouts, Form is not well suited.

There are two options for creating manual layouts.


Template layouts

Taking the Template approach works well and does separate the Page and layout logic. For example:

//EmployeePage.java
public EmployeePage extends Page {

    private Form form;
    
    public void onInit() {
        // Create form
        Form form = new Form("form");
        
        // Add a couple of fields to the form
        form.add(new TextField("firstname"));
        form.add(new TextField("lastname"));
        form.add(new IntegerField("age"));
        form.add(new DoubleField("salary"));

        // Add a submit button to form
        form.add(new Submit("submit", "Add Employee"));

        // Add form the page
        addControl(form);
    }
} 
Lets imagine we want to use a CSS layout using Div <div> and html List <ol> tags.

We could provide the markup for the employee.htm template as shown below, using a template engine like Velocity:

<!--employee.htm-->
${form.startTag()}
    <div style="margin: 1em;">
        <ol>
            <li>
                <label for="firstname">Firstname:</label>
                ${form.fields.firstname}
            </li>
            <li>
                <label for="lastname">Lastname:</label>
                ${form.fields.lastname}
            </li>
            <li>
                <label for="age">Age:</label>
                ${form.fields.age}
            </li>
            <li>
                <label for="salary">Salary:</label>
                ${form.fields.salary}
            </li>
        </ol>
    </div>
    ${form.fields.submit}
${form.endTag()} 
Using a CSS stylesheet, this markup can be transformed into a great looking form as shown in this article.

There are pros and cons to using the template layout approach.

One of the advantages of the Template layout, is that the layout is explicit and one can easily change it to something else. For example instead of using divs and ordered lists, one can change the template to leverage a table layout.

A disadvantage of the Template layout is it introduces redundancy.

In the example above you needed to create the fields in Java, and lay them out using html markup in the template.

If the requirements change to add a new field for example, one will have to add the field in the Page as well as the template. Of course it is possible to "generify" the layout using template engines like Velocity, Freemarker or JSP, since they provide constructs such as iterators and conditional logic. Macro.vm is an example of a generic form layout using Velocity.

Container layouts

To combat the redundency instroduced by the Template approach, you can use Containers for layout purposes.

By using containers, the form and fields could be created and laid out in Java.

Containers such as Div, Span, List, HorizontalPanel and VerticalPanel are ideal for layouts. Click does not yet provide these containers out of the box, but we can put some together as shown below:

// Div.java
// Create a html <div> element
public class Div extends AbstractContainer {

    public String getTag() {
        return "div";
    }
} 
// HtmlList.java
// Create a list <ol> html element, that accepts <li> elements as children
public class HtmlList extends AbstractContainer {

    public String getTag() {
        return "ol";
    }

    // Can only add ListItems: <li> tags
    public Control add(Control control) {
        if (!(control instanceof ListItem)) {
            throw new IllegalArgumentException("Only list items can be added.");
        }
        return super.add(control);
    }
} 
// ListItem.java
// Create a listItem <li> element
public class ListItem extends AbstractContainer {

    public String getTag() {
        return "li";
    }
} 
After creating all the needed containers the form can be assembled.

Continuing with the employee example from the template layout section, the form can be created as follows (note BasicForm is used instead of Form):

// EmployeePage.java
public class EmployeePage extends BasicForm {
    // A form instance variable
    private BasicForm form;

    // Build the form when the page is initialized
    public void onInit() {
        // Create a BasicForm for manual layout
        form = new BasicForm("form");
        
        // Create a list and add it to the form. 
        HtmlList list = new HtmlList();
        form.add(list);
        
        // Add firstname field and pass in its name, label and the list to add the field to
        addTextField("firstname", "Firstname:", list);
        addTextField("lastname", "Lastname:", list);
        addTextField("age", "Age:", list);
        addTextField("salary", "Salary:", list);
        
        // Add a submit button to form
        form.add(new Submit("submit", "Add Employee"));

        // Add the form to the page
        addControl(form);
    }
    
    // Provide a helper method to add fields to the form
    private void addTextField(String nameStr, String labelStr, List list) {
        // Create a new ListItem <li> and add it to the List
        ListItem item = new ListItem();
        list.add(item);

        // Create a textfield with the specified name
        Field field = new TextField(nameStr);
        
        // Create a field label, which associates the label with the field id.
        // label.toString would output: <label for="firstname">Firstname:</name>
        FieldLabel label = new FieldLabel(field, labelStr);

        // Add the label and field to the list item.
        // item.toString would then produce:
        // <li>
        //   <label for="firstname">Firstname:</name>
        //   <input type="text" name="firstname" id="form_firstname" value="" size="20"/>
        // </li>
        //
        item.add(label);
        item.add(field);
    }
} 
Now the employee.htm template would only need to specify the name of the top level container, in this case form.
<!--employee.htm-->
${form}
which produces the following markup:
<!--employee.htm-->
<form method="post" id="form" action="/myapp/employee.htm">
<input type="hidden" name="form_name" id="form_form_name" value="form"/>
    <div style="margin: 1em;">
        <ol>
            <li>
                <label for="firstname">Firstname:</label>
                <input type="text" name="firstname" id="form_firstname" value="" size="20"/>
            </li>
            <li>
                <label for="lastname">Lastname:</label>
                <input type="text" name="lastname" id="form_lastname" value="" size="20"/>
            </li>
            <li>
                <label for="age">Age:</label>
                <input type="text" name="age" id="form_age" value="" size="20"/>
            </li>
            <li>
                <label for="salary">Salary:</label>
                <input type="text" name="salary" id="form_salary" value="" size="20"/>
            </li>
        </ol>
    </div>
    <input type="submit" name="submit" id="form_submit" value="Add Employee"/>
</form> 
Again using a CSS stylesheet, the markup above can be transformed into a great looking form as shown in this article.

You can see a live demo of the above example. Note that the demo adds a couple of features, including error reporting, not covered here.

The advantage of the container approach is that there is no redundancy. Each field is created and added to its container in Java. If new fields are added they will be added to their container in the page. No need to change the template as the layout is taken care of by a combination of markup produced by the container and CSS stylesheets.

A disadvantage is that it is harder to see what output would be rendered by the containers.

Whether you use the template or container layout approach, is up to you. Both work well and have advantages and disadvantages over the other.