From Documentation

Jump to: navigation, search




  • Author
    Hawk Chen, Engineer, Potix Corporation
  • Date
    January 16, 2012
  • Version
    ZK 5

Contents

Introduction

Grails is an open-source web application framework based on the Groovy language. Not just purely a framework, Grails also provides a highly productive development mode including web server, automatic reload of resources and seamless integration with two other best practices, Spring and Hibernate. Grails Server Pages (GSP), which is an evolution from Java Server Pages (JSP) is Grails’ primary view technology.

Compared to JSP, GSP helps simplify implementation procedures and usage of custom tag libraries. It uses "SiteMesh" application framework for page decoration.Please note that only some JavaScript libraries (Prototype and Scriptaculous) can be supported out of the box when utilizing Ajax with GSP. Therefore, developers may have to deal with tedious Ajax details and various JavaScript API integration issues and this is why ZK comes in handy. With the use of a Grails plugin - zkui, developers now empower their level of GSP usage with Ajax by embedding ZK UI components.

This article will demonstrate how ZK can enhance the interactivity of GSP with Ajax. To get a better understanding of this article, it is recommended for readers to know the basic concept of Grails, such as domain class, controller, GSP, and Groovy. The development environment is under Eclipse 3.6, SpringSource Tool Suite 2.7.1., Grails Support 2.7.1., Grails 1.3.7., Groovy-Eclipse Plugin 2.5.1 and zkui 0.3.2.

Ajax GSP with ZK

Prepare Environment

  1. To create a Grails project, we need an eclipse plugin – SpringSource Tool Suite. Click here to download
  2. Install Grails Tools, which is a SpringSource Tool Suite extension. Click here to download
  3. You must install three items: Grails, Grails Support, and Groovy Eclipse


Application Example Background

We are going to build a simple web application called "RaceTrack". "RaceTrack" is designed for a running club which holds several races. The purpose of this example is to show how users of the running club can easily manage their held races through creating, updating and deleting records.

Create a Project

To create the "RaceTrack" project , use eclipse File\New\Grails Project or SpringSource Tool Suite\Create\Grails Project. You must install the zkui plugin in order to harness the power of ZK components.

Right click the "RaceTrack" project in Eclipse and hover to Grails Tools. To install the zkui plugin, type “install-plugin zkui” at the Grails Command Prompt window or search it with the Grails Plugin Manager.

Installation msg.png

You can see the installation message at the Console View. After the plugin is installed, we can start to implement our application.

Create a Domain Class

At the beginning of the implementation process, you have to create a domain class to represent a race and manually add some properties and validation constraint. (Select Grails Tools\Create Domain Class)

 

class Race {
    String name
    Date startDate
    String city
    Integer distance
    Integer cost
    static constraints = {
		name(blank:false, maxSize:50)
		city(inList:["Taichung","Tainan","Kaohsiung"])
		distance(min:0)
		cost(min:0, max:100)
    }
}

Static Scaffolding

With zkui static scaffolding, we can complete the four basic functions: Create, Read, Update and Delete (CRUD) without writing any code. Type "zkui-generate-all racetrack.Race" at the Grails Command Prompt window and zkui will generate 3 GSPs, 1 controller, and 3 composers (under grails-app\composers). To render ZK components in a GSP, you need to add <z:resources/> in the <head> of grails-app\views\layouts\main.gsp.

Static scaffolding.png

Let’s take a closer look at these generated artifacts. The controller class has no big difference with the original Grails controller,but GSP is filled with tags with a prefix “z”. Those tags represent the ZK UI components. The tag name is the component’s name. ZK provides more than 200 components and zkui supports most of them [1] Each GSP has a <z:window> to enclose all ZK components, and the window’s attribute “apply” specifies which composers to handle their events.

 

 <z:window style="padding:5px" apply="racetrack.race.ListComposer">

Hence in list.gsp, the ListComposer will handle events from all components inside <z:window> including the window itself.

Now, run the application and visit the page via the link http://localhost:8080/racetrack/race/create, you will see a form composed by ZK components corresponding to each Race class’ property. We fill out the form partially and then click the “Create” button. Then we can see the validation message pop-up instantly without reloading the page. Now, this GSP is powered by Ajax.

Validation msg.png

Among all the generated artifacts, the composers under grails-app\composers are unfamiliar to Grails developers. This term “composer” comes from ZK. It plays the same role as the Grails controller in a MVC pattern, with a difference in which Grails controller handles requests from GSP while the composer of ZK only handles events issued from ZK components. Another notable difference is that the composer can manipulate ZK components on GSP which we will talk about in the next section.

Composer in Depth

There are 3 composers, and each of them corresponds to one GSP which can be easily identified by its naming. Open ListComposer.groovy which handles events from list.gsp. Let’s first look at these variables.

ListComposer.groovy

Grid grid
Paging paging
Longbox idLongbox

It seems normal, but you’ll find that there are three ZK components whose variable names are identical to that of ZK component tag’s id attribute in list.gsp. This is not a coincidence, it is a feature of the composer called component Auto-wiring. It is automatically enabled by mapping a naming convention and a variable type. Each ZK component has a corresponding class (ex: <z:grid> is org.zkoss.zul.Grid), and if you put the correct name and type in the composer upon your component tag, the zkui plugin will wire components for you. Through these variables we can manipulate ZK components in a composer. In fact, static scaffolding has already done it for you.

You can see a closure afterComposer. This closure is called when all ZK components and its descendants are instantiated and rendered. So we usually do some post-processing to components in it.


ListComposer.groovy

 

def afterCompose = {Component comp ->
        grid.setRowRenderer(rowRenderer as RowRenderer)
        grid.setModel(listModel)
        redraw()
    }
  • Line 2 sets a custom row renderer to grid component and this means that we can draw each row by ourselves.
  • Line 3 attaches a data source that will be used in the renderer.


Then, we look inside the redraw() method:

ListComposer.groovy

 

    private redraw(int activePage = 0) {
        int offset = activePage * paging.pageSize
        int max = paging.pageSize
        def raceInstanceList = Race.createCriteria().list(offset: offset, max: max) {
            order('id','desc')
            if (idLongbox.value) {
                eq('id', idLongbox.value)
            }
        }
        paging.totalSize = raceInstanceList.totalCount
        listModel.clear()
        listModel.addAll(raceInstanceList.id)
    }
  • Line 4 constructs a race list upon search criteria. The only criterion is the race id which we can retrieve from the ZK component by the use of idLongbox.value, we can get the user's inputted id in the textbox.
  • Line 12 changes the Grid’s data source and will cause the grid to re-render.


ListComposer.groovy

 

    private rowRenderer = {Row row, Object id ->
        def raceInstance = Race.get(id)
        row << {
                a(href: g.createLink(controller:"race",action:'edit',id:id), label: raceInstance.id)
                label(value: raceInstance.name)
                label(value: raceInstance.city)
                label(value: raceInstance.distance)
                label(value: raceInstance.cost)
                label(value: raceInstance.startDate)
                hlayout{
                    toolbarbutton(label: g.message(code: 'default.button.edit.label', default: 'Edit'),image:'/images/skin/database_edit.png',href:g.createLink(controller: "race", action: 'edit', id: id))
                    toolbarbutton(label: g.message(code: 'default.button.delete.label', default: 'Delete'), image: "/images/skin/database_delete.png", client_onClick: "if(!confirm('${g.message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}'))event.stop()", onClick: {
                        Race.get(id).delete(flush: true)
                        listModel.remove(id)
                    })
                }
        }
    }

We use rowRenderer to customize Grid’s rendering. For example, to create a ZK component dynamically, zkui provides a leftshift (<<) operator to append a child to each row. The id comes from the grid’s data source. According to the code after line 3, you can see how it adds a link, 4 labels, and 2 tool bar buttons (update, delete) in a row.

Customise grid.png

Instant Deletion

On the second toolbarbutton, the attribute client_onClick allows you to register a client side onClick event handler. The attribute’s value is a snippet of JavaScript to pop-up a confirm dialog box. The prefix client_ is an attribute naming convention to indicate that the event is handled at the client side. The next attribute onClick allows you to register a server-side event handler. This means that when the toolbar button issues an onClick event, a closure will be invoked. If a user clicks the OK button on the confirmation dialog box, the onClick event will propagate to the server and deletes the clicked row from the grid’s data source. This causes the Grid to re-render and then instantly presents the result after deletion without reloading the whole page.

Instant deletion 01.png
Instant deletion 02.png

Real Time Searching

ZK also empowers search function. If we type an existing id and click the search button, we can get the search result displayed in the grid immediately.

Real time searching 01.png

However, searching the race’s id is not practical in this example, we should search by name instead.

In list.gsp, replace <z:longbox id="idLongbox"/> with <z:textbox id=”keywordBox”/>.

Add a variable Textbox keywordBox in the ListComposer.groovy file.

In the redraw() method:

Comment out the 3 lines of code related to idLongBox and add the following 3 lines:

 

if (keywordBox.value){
    ilike('name',"%"+keywordBox.value+"%")
}

Now, our search by name is done.

Real time searching 02.png

You might think that ZK’s capability ends here? No, we can make the search result display to be more interactive.

Register an onChanging event handler by adding the following code in the ListComposer.groovy file:

 
void onChanging_keywordBox(InputEvent e) {
	keywordBox.value = e.value
	redraw()
}

The method will be called when the value of keywordBox changes.

Now, we don't even need the search button, you’ll automatically get the search result when typing.

Real time searching 03.png

Register an event handler in a composer is to make the method name follow the naming convention:

 

void [eventName]_[componentId] (Event e){ 
    … 
}

It is quite simple.

Conclusion

Adopting AJAX can reduce a web application's response time thus improving user experience and communication complexity but various JavaScript libraries of AJAX constitutes a barrier for most Grails developers preventing them from using it. However, with ZK, developers are now able to build more interactive applications easily with many ready-to-use components and leave the communication issues behind. See the complete supported component list. In the next article, I will demonstrate how you can combine all the functions of the 3 GSPs into one page.

Reference

  1. Grails ZK UI Plugin - Reference Documentation http://xiaochong.github.com/zkui/manual/index.html

Resources


Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.
You got stuck here?
Let us know why!
For questions please use the forum