9) Dynamic Components

This chapter focuses on the utility methods that were referenced in the previous chapters. Also we show a few more ways about how you can manipulate the UI components from within your model classes.

9.1) Disabling Components

There are a few ways to disable components:

  • Remove the setter method from the model or make it @Hidden or non public.
  • Leave the value of a nested model null, this will keep the nested controls disabled, because they cannot be accessed anyway.
  • Add the disabled attribute in the HTML file to the appropriate tag.
  • Add a @Disabled annotaton to the field, getter/setter or action method.

These approaches always keep a component more or less statically disabled. To make this a dynamic decision from within the model, you can make use of a disabler utility method:

public String disable<NameOfProperty/NameOfAction>() { ... }

The returned string will be displayed as a tooltip on the disabled component and should state the reason why this is the case. Returning null keeps the component enabled. Here you can also change the return type into a boolean, to make it a binary decision without giving a reason:

public boolean disable<NameOfProperty/NameOfAction>() { ... }

9.2) Hiding Components

Analogous to disabling components, there are a few ways to hide a component:

  • Remove the getter and setter method from the model or make it non public.
  • Add the css style display:none to the component in the HTML or comment out the tag (so that the Wicket ID is still present to let the HTML generator skip generating it again).
  • Add a @Hidden annotaton to the field, getter/setter or action method.

Again you might have the requirement to dynamically hide a component, for this there is also a hider utility method you can implement:

public String hide<NameOfProperty/NameOfAction>() { ... }

This time the returned string is not used anywhere and can only give a hint during code debugging. It is more common to use the boolean alternative:

public boolean hide<NameOfProperty/NameOfAction>() { ... }

Remember that you can make more markup follow a visibility state of a Wicket component by adding the wicket:enclosure tag in your HTML file. So that the label also disappears dynamically when you hide your input.

9.3) Adding Tooltips

Despite adding title attributes to your HTML tags, you can also add the @Tooltip to your getters/setters or action methods. Again if you need a more dynamic way to do this, there is an utility method available for this:

public String get<NameOfProperty>Tooltip() { ... }

For an action method, you have to omit the getter prefix:

public String <NameOfAction>Tooltip() { ... }

9.4) Changing Titles

Every model class, property and action element has a default title representation of the name being printed in a readable manner. To provide your own titles, you have a few options.

  • Add the @Title annotation to replace the automatically generated string representation. Though this will mostly only change the property value in the Wicket internationalization properties file. But it is useful when generated UI models from a UML diagram.
  • Change the Wicket components to use different label models by defining a custom binding via a BindingInterceptor in your pages.
  • Change the internationalization property file value for the corresponding Wicket ID like you are used to from Wicket. The properties file has a higher priority than the title annotation.

Though to have this more dynamic and to maybe even reuse model state inside of titles, you can again implement some utility methods:

public String title() { ... } // for dynamic model class titles and page titles
public String get<NameOfProperty>Title() { ... } // for property titles visible in labels
public String <NameOfAction>Title() { ... } // for action titles visible as button texts 

9.5) Column & Element Order

Normally everything is generated in alphabetical order. The element order can be manually specified using the @ColumnOrder annotation on your classes. Alternatively you can implement a dynamic utility element for this:

public String[] columnOrder() { ... } // can also return Iterable, Collection, etc

This is normally used to define the column order of table models, but you can also use it to define the order of tabbed panes and the order in which the HTML generator generates the tags. In tables and tabbed panes, any element that is missing from the order will automatically be hidden (as an alternative to the other hiding approaches).

9.6) Removing Rows From Tables

Removing a row from a table is possible via action columns. Though the problem is, a row needs to know the list where it is contained for it to be able to remove itself. This binds the row class to the model class where it is displayed as a table, which is sometimes unhandy. To give the outside model complete control over removing rows, there is a utility method you can add next to the table property:

public void removeFrom<NameOfProperty>(<TypeOfProperty> row) { ... }

This makes a special column appear for this action inside the table. You can reference this action column from within a @ColumnOrder via the method prefix "removeFrom" or the constant TableRemoveFromButtonColumnBeanPathElement.COLUMN_ID . Again the Ajax Data Table page provides an example for this. Click here for the source code.

9.7) Date & Number Formatting

You can use the @Format annotation on your property fields and getters/setters to change the presentation of your dates and numbers in the components. The format string follows the standard Java DecimalFormat and SimpleDateFormat conventions.

9.8) Internationalization

Internationalization is mostly what you are used to from Wicket. The addition to consider when working with model classes is that every string returned from a title, tooltip, disabled reason and others can be a property that you want to reference from the Wicket internationalization properties file. See the Form Input page again for an example of this. Click here for the source code.

9.9) Try It

The car below shows examples of a dynamic disable utility method to prevent duplicate actions on the motor state. Also there have been added a few tooltips and a special date format.

public class Car implements Serializable {

    // uninteresting fields, getters and setters omitted

    @Pattern(regexp = "[A-Z]{1,3}-[A-Z]{1,2} [1-9][0-9]{0,3}")
    @Tooltip("Pattern Example: MO-ON 1234")
    public String getLicenseNumber() { return licenseNumber; }

    public String getBrand() { return brand; }

    public String getBrandTooltip() {
        String message = "Popular Brands: ";
        for (final CarBrand brand : CarBrand.values()) {
            message += brand + ", ";
        }
        message = Strings.removeEnd(message, ", ");
        return message;
    }

    @Format("yyyy -_- MM /_/ dd")
    public Date getRegistrationDate() { return registrationDate; }

    public void turnOn() {
        this.state = MotorState.on;
    }

    public String disableTurnOn() {
        if (state == MotorState.on) {
            return "Car is already running";
        } else {
            return null;
        }
    }

    public void turnOff() {
        this.state = MotorState.off;
    }

    public String disableTurnOff() {
        if (state == MotorState.off) {
            return "Car is already stopped";
        } else {
            return null;
        }
    }

}

Hover over the components in the panel below to see the tooltips.