Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

Excerpt
hiddentrue

How to combine different behaviors into one

Panel
borderStylesolid
titleTable of contents
Table of Contents
minLevel1

Howto: Composite Behaviors

It often occurs that multiple wicket behaviors must be used to obtain a certain effect. An example can be a tooltip that requires both a javascript header contribution and an attribute modifier. Another example is that of a confirmation popup when someone clicks on an certain button. Using a composite behavior, which is an example of the composite design pattern, it is possible to realize things like this by adding just a single composite behavior object to a component (e.g. ToolTipBehavior, ConfirmationBehavior) instead of having to add multiple behaviors just to obtain one effect in the user interface. This enhances readability and maintainability of the code.

Example: Confirmation messages

A straightforward example is a confirmation message that asks a user to confirm a (potentially) destructive action before proceeding. One way of doing this is using an attribute on an HTML element that requires confirmation,

No Format
Wiki Markup
{excerpt:hidden=true}How to combine different behaviors into one{excerpt}
{panel:borderStyle=solid|title=Table of contents}{toc:minLevel=1}{panel}

h1.  Howto create and use composite behaviors

It often occurs that multiple wicket behaviors must be used to obtain a certain effect. An example can be a tooltip that requires both a javascript header contribution and an attribute modifier. Another example is that of a confirmation popup when someone clicks on an certain button. Using a composite behavior, which is an example of the composite design pattern, it is posisble to realize things like this by adding just a single composite behavior object to a component (e.g. ToolTipBehavior, ConfirmationBehavior) instead of having to add multiple behaviors just to obtain one effect in the user interface. This enhances readability and maintainability of the code. 

h2. Example: Confirmation messages

A straightforward example is a confirmation message that asks a user to confirm a (potentially) destructive action before proceeding. One way of doing this is using an attribute on an HTML element that requires confirmation,
{noformat}
   <a href="..." confirmationMessage="Are you sure?">link text</a>
{noformat}

together

...

with

...

javascript

...

that

...

inspects

...

the

...

confirmationMessage

...

attribute

...

and

...

when

...

it's

...

there

...

intercepts

...

the

...

onClick

...

event

{
No Format
}


function confirmModifyOnclickImpl(element) {
	if (element.nodeType == 1) {
		var attribute = element.getAttribute("confirmationMessage");
		if (attribute) {
			var oldonclick = element.onclick;
			element.onclick = function () {
				var result = confirm(attribute);
				if (!result) {
					return result;
				}
				if (oldonclick) {
					return oldonclick();
				} else {
					// no special actions
				}
			};
		}
	}
	if ((element.nodeType == 1) || (element.nodeType == 9)) {
		var children = element.childNodes;
		for (var i = 0; i < children.length; i++) {
			confirmModifyOnclickImpl(children[i]);
		}
	}
}
function confirmModifyOnclick() {
	confirmModifyOnclickImpl(document);
}

runOnLoad(confirmModifyOnclick);
{noformat}

In

...

the

...

above

...

example,

...

the

...

runOnLoad

...

function

...

is

...

a

...

function

...

defined

...

elsewhere

...

that

...

makes

...

sure

...

that

...

the

...

confirmModifyOnclick

...

function

...

is

...

run

...

when

...

the

...

document

...

is

...

loaded.

...

This

...

function

...

modifies

...

all

...

onClick

...

event

...

handlers

...

for

...

elements

...

that

...

have

...

the

...

confirmationMessage

...

attribute

...

to

...

display

...

a

...

confirmation

...

message

...

before

...

proceeding,

...

allowing

...

the

...

user

...

to

...

cancel.

The standard approach in wicket

The standard approach in wicket is to implement this with behaviors. In this case, we need to add two behaviors to a component that requires confirmation:

No Format
 

h2. The standard approach in wicket

The standard approach in wicket is to implement this with behaviors. In this case, we need to add two behaviors to a component that requires confirmation: 
{noformat}
  link.add(HeaderContritor.forJavaScript(MyClass.class, "confirm.js"));
  link.add(new AttributeModifier("confirmationMessage", true, new Model(message));  
{noformat}

Nevertheless,

...

this

...

approach

...

is

...

a

...

bit

...

problematic

...

for

...

the

...

following

...

reasons:

...

  • You

...

  • need

...

  • to

...

  • do

...

  • two

...

  • things

...

  • (add

...

  • two

...

  • behaviors)

...

  • just

...

  • to

...

  • accomplish

...

  • one

...

  • task

...

  • (confirmation).

...

  • If

...

  • in

...

  • the

...

  • future

...

  • the

...

  • implementation

...

  • of

...

  • confirmation

...

  • popups

...

  • changes

...

  • code

...

  • might

...

  • be

...

  • changed

...

  • in

...


  • many

...

  • places

...

  • to

...

  • accomplish

...

  • this.

...

It

...

would

...

be

...

better

...

to

...

use

...

one

...

behavior

...

and

...

to

...

write

...

code

...

like

...

this:

{
No Format
}
  link.add(new ConfirmationBehavior("message");
{noformat}

In

...

this

...

way,

...

any

...

implementation

...

changes

...

are

...

localized

...

in

...

the

...

confirmation

...

behavior

...

and

...

the

...

code

...

is

...

conceptually

...

close

...

to

...

reality:

...

we

...

must

...

do

...

one

...

thing

...

in

...

the

...

code

...

to

...

accomplish

...

one

...

thing

...

in

...

the

...

application.

...

Composite behavior,

...

first

...

attempt

A solution is to use the composite design pattern and create a composite behavior.
This can be done as follows:

No Format


A composite behavior is fairly easy to implement. 

{noformat}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import wicket.Component;
import wicket.Response;
import wicket.behavior.IBehavior;
import wicket.markup.ComponentTag;
import wicket.markup.html.IHeaderContributor;

/**
 * Represents a composite behavior allowing the user to attach multiple behaviors to a 
 * component at once. 
 *
 */
public class CompositeBehavior implements IBehavior, IHeaderContributor {
	
	private List<IBehavior> _behaviors; 
	
	public CompositeBehavior(IBehavior[] aBehaviors) { 
		_behaviors = new ArrayList<IBehavior>(Arrays.asList(aBehaviors));
	}
	
	public void add(IBehavior aBehavior) { 
		_behaviors.add(aBehavior);
	}

	public void bind(Component aComponent) {
		for (IBehavior behavior: _behaviors) { 
			behavior.bind(aComponent);
		}
	}

	public void detachModel(Component aComponent) {
		for (IBehavior behavior: _behaviors) { 
			behavior.detachModel(aComponent);
		}
	}

	public void exception(Component aComponent, RuntimeException aException) {
		for (IBehavior behavior: _behaviors) { 
			behavior.exception(aComponent, aException);
		}
	}

	public void onComponentTag(Component aComponent, ComponentTag aTag) {
		for (IBehavior behavior: _behaviors) { 
			behavior.onComponentTag(aComponent, aTag);
		}
	}

	public void rendered(Component aComponent) {
		for (IBehavior behavior: _behaviors) { 
			behavior.rendered(aComponent);
		}
	}

	public void renderHead(Response aResponse) {
		for (IBehavior behavior: _behaviors) {
			if ( behavior instanceof IHeaderContributor) { 
			    ((IHeaderContributor)behavior).renderHead(aResponse);
			}
		}
	}

}
{noformat}

At

...

construction,

...

the

...

behavior

...

is

...

passed

...

a

...

list

...

of

...

behaviors

...

that

...

it

...

must

...

use.

...

In

...

the

...

bind

...

method

...

of

...

the

...

composite

...

behavior,

...

the

...

composite

...

behavior

...

adds

...

each

...

of

...

its

...

individual

...

behaviors

...

to

...

the

...

component

...

to

...

make

...

sure

...

they

...

are

...

used.

...

Note

...

that

...

the

...

composite

...

behavior

...

also

...

implements

...

IHeaderContributor

...

so

...

that

...

header

...

contributions

...

are

...

also

...

taken

...

into

...

account.

...

Using

...

this

...

composite

...

behavior,

...

defining

...

a

...

confirmation

...

behavior

...

is

...

just

...

as

...

easy

...

as

...

creating

...

a

...

subclass

...

and

...

passing

...

in

...

the

...

two

...

behaviors

...

that

...

it

...

requires.

...

Composite behavior,

...

now

...

it

...

works...

...

Nevertheless,

...

we

...

are

...

not

...

there

...

yet.

...

The

...

composite

...

behavior

...

of

...

the

...

previous

...

example

...

has

...

one

...

flaw

...

and

...

this

...

is

...

that

...

behaviors

...

themselves

...

cannot

...

be

...

internationalized.

...

The

...

problem

...

is

...

that

...

behaviors

...

are

...

constructed

...

in

...

a

...

composite

...

behavior

...

subclass

...

before

...

their

...

parent

...

component

...

is

...

known.

...

As

...

a

...

result,

...

it

...

is

...

impossible

...

to

...

use

...

internationalization.

...

And

...

internationalization

...

is

...

required

...

for

...

the

...

confirmation

...

behavior

...

if

...

we

...

want

...

to

...

internationalize

...

the

...

confirmation

...

messages.

...

The

...

solution

...

is

...

simple.

...

Instead

...

of

...

creating

...

the

...

behaviors

...

at

...

constructions

...

of

...

the

...

composite

...

behavior

...

we

...

need

...

to

...

delay

...

adding

...

them

...

until

...

their

...

parent

...

component

...

is

...

known,

...

and

...

this

...

is

...

in

...

the

...

bind

...

method.

...

So

...

we

...

modify

...

the

...

composite

...

behavior

...

as

...

follows:

No Format
 
{{
  public class CompositeBehavior implements IBehavior, IHeaderContributor {
	
	private List<IBehavior> _behaviors; 
	
	public CompositeBehavior() { 
		_behaviors = new ArrayList<IBehavior>();
	}

        ....

        public void bind(Component aComponent) {
                for (IBehavior behavior: createLocalizedBehaviors(aComponent) ) {
                        behavior.bind(aComponent);
                        add(behavior);
                }
        }

        /**
         * Callback to create further localized componentsbehaviors.
         * @return Array of behaviors. The bind() method is called on each behavior
         *   returned from this method.
         */
        protected abstract IBehavior[] createLocalizedBehaviors(Component aComponent);

        ....
} 
}}

Now

...

the

...

subclass

...

of

...

CompositeBehavior

...

should

...

implement

...

createLocalizedBehaviors

...

to

...

create

...

the

...

behaviors

...

it

...

requires.

...

Using

...

the

...

component

...

which

...

is

...

passed

...

in,

...

localization

...

can

...

be

...

done.

...