Grails with ZK: How To Create Components in Runtime with The Builder
ZKGrails plugin has an extremely nice feature which make easier than ever to create ZK components in runtime.
First lets see how is the usual way to create components in runtime using Java. This is the zul representation of what we’re trying to achieve:
<div>
<toolbarbutton label="Clickable Item" href="#" />
<toolbarbutton image="edit.png" if="${userHasAccess}" />
<toolbarbutton image="delete.png" if="${userHasAccess}" />
</div>And the java code to do the same:
Div div = new Div();
Toolbarbutton tbb = new Toolbarbutton("Clickable Item");
tbb.setHref("#");
tbb.setParent(div);
if (userHasAccess) {
Toolbarbutton tbbEdit = new Toolbarbutton();
tbbEdit.setImage("edit.png");
tbbEdit.setParent(div);
Toolbarbutton tbbDelete = new Toolbarbutton();
tbbDelete.setImage("delete.png");
tbbDelete.setParent(div);
}As you can easily see the java code is pretty verbose. Using ZKBuilder the code will be easier to understand and smaller, take a look:
Div div = new Div();
div.append {
toolbarbutton(label: "Clickable Item", href: "#")
if (userHasAccess) {
toolbarbutton(image: "edit.png")
toolbarbutton(image: "delete.png")
}
}Which one do your prefer?
If you want more continue reading to see how to use the builder as a template to a list of items.
Using ZKBuilder as a Template
Handling events on ZK macro components
One of the great features of ZK is the possibility to create a new component based only on other existing components using the declarative language ZUML - the same you've been using in you zul files. Those are called Macro Components. The beauty of macro components is that it's very easy to make a new one and hence avoid code duplication on your pages.
Talk about how to implement a macro component isn't the scope of this post, so I suggest you to read the documentation before continue reading.
We'll work on a very simple macro component. But, don't hesitate when you need to make a very complex one. Our macro component is this: WEB-INF/mymacro.zul
<hbox>
<label id="myLabel" value="Click the button"/>
<button id="myButton" label="The Button"/>
</hbox>Now that you already knows what is and how to make a macro component, let's start talking about what you may think it'll work but actually it doesn't.
Automatically Forward Event Doesn't Work
The standard way to register events on ZK's component, using MVC, is to use the "autowire" feature of GenericForwardComposer. Like this:
WEB-INF/index.zul
<?xml version="1.0" encoding="UTF-8"?>
<zk>
<window apply="com.felipecypriano.IndexComposer">
<label id="lblOutside"/>
<button id="btnOutside" label="Not In Macro"/>
</window>
</zk>src/com/felipecypriano/IndexComposer.java
package com.felipecypriano;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Label;
public class IndexComposer extends GenericForwardComposer {
private Label lblOutside;
public void onClick$btnOutside() {
lblOutside.setValue("Button 'not in macro' clicked");
}
}The button 'btnOutside' belongs to the window component and therefore its composer can automatically access and configure btnOutside events. When the Composer is initializing it detects the method onClick$btnOutside() and sets this method as a 'btnOutside' onClick event listener, in other words when the component is clicked this method is executed.
Knowing this we can think that add an event to 'myButton', which belongs to 'mymacro', is pretty much the same. Don't we? Let's add the macro in the index.zul and create a new method to handle onClick event of 'myButton':
WEB-INF/index.zul
<?xml version="1.0" encoding="UTF-8"?>
<?component name="mymacro" macroURI="/mymacro.zul" ?>
<zk>
<window apply="com.felipecypriano.IndexComposer">
<label id="lblOutside"/>
<button id="btnOutside" label="Not In Macro"/>
<separator bar="true"/>
<mymacro/>
</window>
</zk>Just before <zk> tag is the macro component definition (line 2), this directive tells to ZK where is the file that contains the component - macroURI="/mymacro.zul" - and what's the tag name we'll use - name="mymacro" - to reference our component (line 8).
Adding the onClick event on 'myButton' cannot be done by just adding this method to IndexComposer:
src/com/felipecypriano/IndexComposer.java
public void onClick$myButton() {
lblOutside.setValue("Button 'myButton' clicked");
}So, if this doesn't work what should I do?
Grails with ZK: Embedding ZUL in GSP
How about zk pages and gsp were so friends that we could use both together? Imagine the possibility to use Sitemesh to decorate your page or use UrlMapping to choose what the URL will be. Or you could move your project to ZK painlessly, you could update the pages one by one and the new code will work side by side.
ZKGrails plugin 0.7.6 has been released with this great new feature, you can use two simple tags to include any zul page in your GSP. To include the necessary css and javascript into head use <z:head/> and to insert the content anywhre you want in the body of your gsp you'll use <z:body/>.
To see it in practice let's change Grails Quick Start. Follow the steps described there and after you complete all the steps, go back here to update the list.gsp to use ZK Grid component. Don't forget to use Grails 1.1.2 and ZKGrails 0.7.6 at least.
Embedding ZK in GSP
Firstly create this file: grails-app/conf/BuildConfig.groovy and configure ZKGrails repository:
grails.plugin.repos.discovery.zkgrails = "http://zkgrails.googlecode.com/svn/plugins" grails.plugin.repos.resolveOrder = ['zkgrails','default','core']
Now you can install ZKGrails version 0.7.6 by executing grails install-plugin zk 0.7.6. After installing ZK plugin, create the zul page we'll use: web-app/book/list.zul. You can let this file blank for now, before editing it we need to change automatic scaffolding by creating the the gsp pages executing this command:
grails generate-views Book
The command will create 4 gsp files in grails-app/views/book: create.gsp, edit.gsp, list.gsp and show.gsp. We will change the list.gsp to instead of use simple HTML table we'll use ZK components. Now let's get back to our web-app/book/list.zul, the objective is to create a grid using zk components and to add a new functionality to delete all selected books. Open list.zul file and put the following code in it:
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" arg0="./wnd"?>
<zk xmlns:n="http://www.zkoss.org/2005/zk/native">
<window id="wnd" apply="${bookListComposer}">
<listbox id="listBoxBooks" model="@{wnd$composer.booksModel}" checkmark="true" multiple="true"
fixedLayout="true" width="500px">
<listhead>
<listheader label="ID" sort="auto(id)" width="50px"/>
<listheader label="Author" sort="auto(author)" width="225px"/>
<listheader label="Title" sort="auto(title)" width="225px"/>
</listhead>
<listitem self="@{each=book}" value="@{book}">
<listcell label="@{book.id}"/>
<listcell label="@{book.author}"/>
<listcell label="@{book.title}"/>
</listitem>
</listbox>
<n:span class="buttons">
<button id="btnDelete" sclass="delete" mold="os" label="Delete Selected"/>
</n:span>
</window>
</zk>Next step is create the composer for this list.zul page, the composer is the thing that controls the page's behavior, just like a controller. Create a BookListComposer.groovy file in grails-app/composers, and this is the content:
import org.zkoss.zkgrails.*
import org.zkoss.zkplus.databind.BindingListModelList
class BookListComposer extends GrailsComposer {
def wnd
def listBoxBooks
def booksModel
def binder
def afterCompose = {
booksModel = new BindingListModelList([], true)
reloadBooks()
binder = wnd.getVariable("binder", true)
}
public void onClick_btnDelete() {
if (listBoxBooks.selectedCount > 0) {
listBoxBooks.selectedItems.each { listItem ->
def book = listItem.value
book.delete(flush: true)
}
reloadBooks()
}
}
private def reloadBooks() {
def books = Book.list()
booksModel.clear()
booksModel.addAll(books)
binder?.loadAll()
}
}Now we only need to put the zul page in the list.gsp replacing the HTML <table> tag to <z:body />, the final code is this:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="layout" content="main" />
<z:head />
<title>Book List</title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${resource(dir:'')}">Home</a></span>
<span class="menuButton"><g:link class="create" action="create">New Book</g:link></span>
</div>
<div class="body">
<h1>Book List</h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<z:body />
</div>
</body>
</html>As you can see - lines 5 and 19 - we only need to put the tags where we want it's content to be in the page. But you might be wondering where is the path to list.zul file? By default the same convention used by Grails' view, so for our example both tags - head and body - automatically resolves to /book/list.zul. And if you need to specify the files you want you can by setting the attribute zul, like:
<z:body zul="/path/to/file.zul" />
Now we can mashup our both favorite frameworks to make our projects look and behave even better.
# Update 1
Fixed a missing namespace on zul file; and the binder call on BookListComposer. Thanks André.
# Update 2
GitHub repository with full source code used to made this post: http://github.com/fmcypriano/embedding-zul-gsp
Grails with ZK: Where are my Controllers?
ZK's zul pages works very differently compared to Grails' gsp pages but they have one thing in common both have a class to control the page's state, the behavior of each piece, the data that is showed or is being inserted by the user and this class is called Composer and Controller respectively. This is the second article about my work with ZK and Grails, read the first part before reading this one to get an overview: Grails with ZK Understanding Both Together.
"A [grails] controller handles requests and creates or prepares the response. They can generate the response or delegate to a view.", this is the definition taken from grails website. In other words the controller receives a request do what it has to do and sends the response back.
ZK's composers are a little different, they can do things that you'd need javascript to do using gsp and controllers. The main power of a controller is that you've full control of all components in the page without need to write any javascript code. You could add or remove any component dynamically from the page, register events and whatever more you java and groovy skills let's you do.
Pages Shouldn't Have Any Code
In both technologies you can embed code directly in the page, in gsp you can use scriptlets:
<% items.each {
total = it.quantity * it.price
}%>An in zul pages you can use zkscript tags:
<zkscript>
items.each {
total = it.quantity * it.price
}
</zkscript>It's fairly easy to use code inside the pages and I really do believe you shouldn't do this, it makes your code harder to read and a mess to maintain.
The only places where this kind of code is good is in tutorials and that's why you'll see lots of tutorials using it, but in your actual project the composer/controller should do this job always.
Your First Composer
To demonstrate what a composer does lets create a simple editable page. The page will show a text to the user an the user can click on some buttons to change the text. All the actions will happen without refresh and you won't need to write any javascript.
Create a file named editable.zul in <your-project>/web-app/ with this content:
<your-project>/web-app/editable.zul
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window id="wndEditable" title="Editable Text" apply="${editableComposer}">
<div id="divText"/>
<button id="btnEdit" label="Edit"/>
<button id="btnSave" label="Save" visible="false"/>
<button id="btnCancel" label="Cancel" visible="false"/>
</window>Pay attention to the id attribute it's value will be used in the composer to get the java object corresponding to the component. We create an empty div called 'divText' just to make sure where the text will be in: at the top, before the buttons.
Now let's go to the composer - hooray finally eh -. Create a new directory in grails-app folder called composers and inside this new directory create a file called EditableComposer.groovy.
grails-app/composers/EditableComposer.groovy
import org.zkoss.zkgrails.GrailsComposer
import org.zkoss.zul.*
import org.zkforge.fckez.FCKeditor
class EditableComposer extends GrailsComposer {
def divText
def btnEdit
Button btnSave
def btnCancel
FCKeditor editor
String text
def afterCompose = {
// This is code execute just after all the components are created, so you can access them safely
showText()
viewMode true
}
void onClick_btnEdit() {
if (!editor) {
editor = new FCKeditor()
}
editor.value = text
divText.children.clear()
divText.appendChild editor
viewMode false
}
void onClick_btnSave() {
text = editor.value
showText()
viewMode true
}
void onClick_btnCancel() {
showText()
viewMode true
}
private def showText() {
text = text ?: "This is my first Composer"
divText.children.clear()
divText.appendChild new Html(content: text)
}
private def viewMode = { isViewing ->
btnEdit.visible = isViewing
btnSave.visible = !btnEdit.visible
btnCancel.visible = btnSave.visible
}
}The components in zul are injected in composer's attributes with the same name as the id on zul file. On line 8 we use a specific type to declare the btnSave, this is just to show you that for each component in zul there's a corresponding java class where you can access and/or change all the attributes. You don't need to worry about thread safety here because for each accessed page a new composer is instantiated, this commonly known as prototype scope.
The afterCompose closure - lines 14 through 18 - is execute just after all the components declared in the zul are created and ready to use, here is the right place to put all your initialization code.
One of the features that I really like is how we can declare events to each component. As you can see in lines 20, 32 and 38 we've declared events to listen to the user clicks on the button for each of the 3 buttons. Basically this is how you declared all kinds events for all components:
public void onEventName_componentId(Event event)
Before the underscore character is the event name and after is the component id. You can set events even for components that you didn't declared as composers attributes.
You don't need to worry about javascript to change the state in the client-side ZK will handle it for you, just change the attributes values in your groovy code and you're done. I highly recommend you give ZK a try and start playing with it, if the application you're developing fits this desktop like model ZK is a great framework to boost your productivity.
See Also:
