Envisage ZK 6: An Annotation Based Composer For MVC"

From Documentation
Line 37: Line 37:
 
But wait, no.  
 
But wait, no.  
  
The unordinary part of the story is here: this feature is actually implemented by just 8 lines of code.
+
The unordinary part of the story is here: this feature is actually implemented by just 9 lines of code.
  
 
<source lang="java">
 
<source lang="java">

Revision as of 07:59, 18 January 2011

DocumentationSmall Talks2011JanuaryEnvisage ZK 6: An Annotation Based Composer For MVC
Envisage ZK 6: An Annotation Based Composer For MVC

Author
Simon Pai, Engineer, Potix Corporation
Date
January 19, 2011
Version
ZK 6

The Story

Think of an extremely ordinary scenario, where you want to clear all the texts in a form by clicking a button.

SmallTalk Selector Demo Form.png

While you see the screen shot, you may have already come up with an implementation in mind. For example:

public class SomeFormController extends GenericForwardComposer {
	
	Textbox usenameTb;
	Textbox passwordTb;
	Textbox retypepwTb;
	// ...
	// ...
	Textbox memoTb;
	
	public void onClick$clearBtn(Event event) {
		usenameTb.setValue("");
		passwordTb.setValue("");
		retypepwTb.setValue("");
		// ...
		// ...
		memoTb.setValue("");
	}
	
}

But wait, no.

The unordinary part of the story is here: this feature is actually implemented by just 9 lines of code.

public class FormController extends GenericAnnotatedComposer {
	@Wire("textbox, intbox, decimalbox, datebox")
	List<InputElement> inputs;

	@Listen("onClick = button[label='Clear']")
	public void onClear(MouseEvent event) {
		for(InputElement i : inputs) i.setText("");
	}
}

These are what we foresee in ZK 6: leveraging Annotation power from Java 1.5, and introduction to some new techniques.

In this Small Talk we are going to reveal two new weapons: Selector and GenericAnnotatedComposer.


 

The jQuery/CSS3-like Component Selector

In the previous example, Selector is shown as a part of the parameters in Annotation @Wire and @Listen.

	@Wire("textbox, intbox, decimalbox, datebox, spinner")
	@Listen("onClick = button[label='Clear']")

The concept is simple: Selector is a pattern string that matches nodes in a Component tree.

In other words, by giving a Selector string, you can specify a collection of Components from a ZUL file.

	// Collects all the textboxes, intboxes, decimalboxes, dateboxes, and spinners as a List and wire to inputs
	@Wire("textbox, intbox, decimalbox, datebox, spinner")
	List<InputElement> inputs;
	
	// Collects all the buttons whose label is "Clear", and adds EventListeners for them
	@Listen("onClick = button[label='Clear']")
	public void onClear(MouseEvent event) {
		// ...
	}

If you know jQuery or CSS selector, this is exactly their counterpart on server side.


Syntax

The syntax of Selector is closely analogous to CSS3 selector.

Component type, class, attribute, pseudo class are used to describe properties of a component. For example:

// Matches any Button component
"button"

// Matches any Component with ID "btn"
"#btn"

// Matches any Button with ID "btn"
"button#btn"

// Matches any Button whose label is "Submit"
"button[label='Submit']"

Combinators are used to describe relations between components. For example:

// Matches any Button who has a Window ancestor
"window button"

// Matches any Button whose parent is a Window
"window > button"

// Matches any Button whose previous sibling is a Window
"window + button"

// Matches any Button who has a Window as a senior sibling
"window ~ button"

// Matches any Button whose parent is a Div and grandparent is a Window
"window > div > button"


 

GenericAnnotatedComposer

GenericAnnotatedComposer is analogous to GenericForwardComposer. But instead of wiring variables by naming convention, the new composer wires them by annotation and specifies the Components by Selector.

public class MyComposer extends GenericAnnotatedComposer {
	
	// If the field is a Collection, the composer will wire all Components matched by the selector
	@Wire("label")
	private <List>Label labelList;
	
	// Same for Array
	@Wire("label")
	private Label[] labelArray;
	
	// If the field is not a Collection or Array, the first matched Component is wired to the field
	@Wire("label[value='zk']")
	private Label label1;
	
	// If selector string is not given, it will attempt to wire implicit objects by name or fellows by ID.
	@Wire
	private Desktop desktop;
	@Wire
	private Button clearBtn;
	
}

Event listening is handled in a similar way. However, instead of forwarding the events, it adds the method to the EventListener of the target Components directly.

public class MyComposer extends GenericAnnotatedComposer {
	
	// Like auto-forwarding, methods annotated with @Listen will be added to the event listeners of the components described by selector.
	@Listen("onClick = button#btn")
	public void onPressButton(Event event) {
		// The event here will be the original event, not ForwardEvent!
	}
	
	// The event listener will be added to ALL the components that match the selector, not just the first match
	@Listen("onClick = #myGrid > rows > row")
	public void onClickAnyRow(SelectEvent event) {
		// ...
	}
	
	// You can specify multiple event types
	@Listen("onClick = button[label='Submit']; onOK = textbox#password")
	public void onSubmit(Event event) {
		// ...
	}
	
}


 

More about Selector

You can also use Selector independently.

For example:

Window win;

// returns a list of components, containing all labels in the page
Selectors.find(page, "label");

// returns all components with id "myId" under the Window win. (including itself)
Selectors.find(win, "#myId");

// returns all components whose .getLabel() value is "zk" (if applicable)
Selectors.find(page, "[label='zk']");

// returns all captions whose parent is a window
Selectors.find(win, "window > caption");

// returns all buttons and toolbarbuttons
Selectors.find(page, "button, toolbarbutton");

// you can assemble the criteria:
// returns all labels, whose parent is a window of id "win", and whose value is "zk"
Selectors.find(page, "window#win > label[value='zk']");


Comparison with CSS3 Selector

Syntax In CSS 3 Selector In Component Selector Comment
tagname DOM element type Component type  
#id DOM ID Component ID  
.class CSS class SClass / ZClass  
[attr='value'] DOM attribute getAttr() or dynamic attribute If getAttr() is not a method on such component, it is skipped
:pseudo-class Pseudo class Pseudo class :root, :empty, :first-child, :last-child, :only-child, :nth-child(), :nth-last-child()
::pseudo-element Pseudo element N/A  
> + ~ Combinator Combinator Identical to CSS 3 combinators


 

Download

Although Selector and GenericAnnotatedComposer is design for ZK 6, they are also ready for ZK 5.0.5 or above. Just include the jar file in WEB-INF/lib directory and make sure your web application is running on JVM 1.5 or above.

The jar file, source and javadoc is available on Google Code.