(Quick Reference)

3 Composer - Reference Documentation

Authors: groovyquan

Version: 0.5.1

3 Composer

A composer is responsible to initialize a component (or a component of tree) when ZK loader is composing a component. It is the controller in the MVC pattern, while the component is the view,which separate the code from the user interfaces.

3.1 Creating a composer

Composers can be created with the create-composer target. For example try running the following command from the root of a Grails project:
grails create-composer window

The command will result in the creation of a composer at the location grails-app/composers/WindowComposer.groovy:

class WindowComposer {
    def afterCompose = {Component comp ->
        // initialize components here
    }
}
WindowComposer by default provide a afterCompose closure to initialize components.

The create-composer command is merely for convenience and you can just as easily create composers using your favorite text editor or IDE

3.2 Apply a composer

Each component Taglib has a apply attribute,so you can place the composer here
<z:window id="myWindowComposer" apply="package.WindowComposer"></z:window>

In the WindowComposer side

def afterCompose = {Component comp ->
    assert comp.id=="myWindowComposer"
    assert (comp instanceof org.zkoss.zul.Window)
}

3.3 Auto-wired Component

If apply a composer to a Component that implement a IdSpace Interface,such as org.zkoss.zul.Window. The Component's children can be auto-wired to composer's fields

The following is an example. The onChange event received by Textbox mytextbox will be forwarded to target Window mywin as a new target event onChange_mytextbox and the Textbox component with id name "mytextbox" and Label with id name mylabel are injected into the "mytextbox" and "mylabel" fields respectively(so you can use mytextbox and mylabel variable directly in onChange_mytextbox without problem).

Composer

class MyComposer{
    Textbox mytextbox
    Window self //embeded object, the supervised window "mywin"
    Page page //the ZK page
    Label mylabel

def afterCompose = {Component comp -> assert mytextbox.id=="mytextbox" assert mylabel.id=="mylabel" }

def onChange_mytextbox(Event event) { mylabel.setValue("You just entered: "+ mytextbox.getValue()) } }

View

<z:window id="mywin" apply="MyComposer">
    <z:textbox id="mytextbox"/>
    <z:label id="mylabel"/>
</z:window>

3.4 Redirects

Actions can be redirected using the redirect method present in all composers

The parameters of redirect is same as controller's redirect

Composer

class MyComposer{
    Button mybutton

def afterCompose = {Component comp ->

}

def onClick_mybutton(Event event) { redirect(controller: 'demo', action: 'index', id: 1) } }

following code same as onClick_button above

def afterCompose = {Component comp ->
    mybutton.addEventListener('onClick'){
        redirect(controller: 'demo', action: 'index', id: 1)
    }
}

View

<z:window id="mywin" apply="MyComposer">
    <z:button label="mybutton"/>
</z:window>

3.5 Data Binding

To uses Grails' underlying data binding capability,zkui injection a getParams method to Component.

Domain Class

class Person {
    String firstName
    String lastName
    String fullName
    static constraints = {
    }
}

View

<z:window id="mywin" apply="MyComposer">
    <z:textbox name="firstName"/>
    <z:textbox name="lastName"/>
    <z:textbox name="fullName"/>
    <z:button label="submit"/>
</z:window>

Composer

class MyComposer{
    Button submit
    def afterCompose = {Component mywin ->
        submit.addEventListener('onClick'){
            def person=new Person(mywin.params)
            …
        }
    }
}

A bindData method same as in controller also provide to Composer

def p = new Person()
bindData(p, mywin.params)

3.6 Unit Test

When use grails create-composer create a composer,a unit class that is a sub-class of ComposerUnitTestCase also created

It provides a mockComposer methods for mocking zkui's Selector,Builder and so on.

mockComposer(MyComposer)
def myComposer=new MyComposer
...

3.7 Render Errors

zkui injection a renderErrors method to Component.

If you have Domain class

class Book{
    String author
    String title
}

In View

<z:window id="formWindow" title="demo" apply="your.Composer">
    <z:textbox name="author"/>
    <z:textbox name="title"/></z:window>

Then in your.Composer use renderErrors

if (!book.save()) {
            formWindow.renderErrors(bean: book)
 }

In addition,you can also use grails's traditional renderErrors

In your.Composer

if (!book.save()) {
    flash.book = book
    redirect(controller: "book", action: "edit", id: book.id)
 }

In your view

<g:hasErrors bean="${flash.book}">
<div class="errors">
    <g:renderErrors bean="${flash.book}" as="list" />
</div>
</g:hasErrors>

3.8 ZK's old Data Binding

Data binding is a mechanism that automates the data-copy plumbing code (CRUD) between UI components and the data source. Application developers only have to tell the data binding manager about the associations between UI components and the data source. Then, the data -binding manager will do all the loading (loading data from the data source to UI components) and saving (saving data from UI component into the data source) jobs automatically.

Activates Data Binding Manager

<z:window  apply="org.zkoss.demo.MyComposer,org.zkoss.zkplus.databind.AnnotateDataBindingComposer">
</z:window>

For more information, please refer to the relative blog post Databinding Composer

Associate UI Components with Data Source

After activating the data-binding manager, you have to define the required UI objects and then associate them with the data source.

In the gsp view:

<z:window id="win" apply="test.TestComposer,org.zkoss.zkplus.databind.AnnotateDataBindingComposer">
    <z:textbox value="@{win#composer.username}"/>
<z:/window>

In the test.TestComposer:

class TestComposer{

def username="test"

def afterCompose = {Component comp -> … } }

See more ZK Developer's Reference/Data Binding

3.9 The Next Generation Data Binding System

Introduction

ZK Bind is a whole new data binding system with new specifications and implementations.

For details

Envisage ZK 6: The Next Generation Data Binding System

3.10 An Annotation Based Composer For MVC

The Story

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

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

class SomeFormComposer {

Textbox usenameTb Textbox passwordTb Textbox retypepwTb // … // … Textbox memoTb

def afterCompose = {Component comp -> // initialize components here }

def 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.

class FormComposer {
    @Wire("textbox, intbox, decimalbox, datebox")
    List<InputElement> inputs

@Listen("onClick = button[label='Clear']") def 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.

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")
@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 gsp file.

// Collects all the textboxes, intboxes, decimalboxes, and dateboxes as a List and wire to inputs
@Wire("textbox, intbox, decimalbox, datebox")
List<InputElement> inputs

// Collects all the buttons whose label is "Clear", and adds EventListeners for them @Listen("onClick = button[label='Clear']") def 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"

Comparison with CSS3 Selector

SyntaxIn CSS 3 SelectorIn Component SelectorComment
tagnameDOM element typeComponent type 
#idDOM IDComponent ID 
.classCSS classSClass / ZClass 
attr='value'DOM attributegetAttr() or dynamic attributeIf getAttr() is not a method on such component, it is skipped
:pseudo-classPseudo classPseudo class:root, :empty, :first-child, :last-child, :only-child, :nth-child(), :nth-last-child()
::pseudo-elementPseudo elementN/A 
> + ~CombinatorCombinatorIdentical to CSS 3 combinators

For more

Envisage ZK 6: An Annotation Based Composer For MVC

3.11 Support extends GenericForwardComposer or other Composer

In Grails ZK UI 0.5,support extends GenericForwardComposer or other Composer,so can take advantage of the GenericForwardComposer

In grails-app/composers

class TestComposer extends GenericForwardComposer {

TestComposer() { super('_' as char) }

@Override void doAfterCompose(Component comp) { super.doAfterCompose(comp) // initialize components here }

}

Equivalent to the following

class TestComposer {

def afterCompose = {comp -> // initialize components here } }