9/14/2012

เบาๆ กับ ZKGrails

ช่วงนี้ได้เล่น Grails มากขึ้นและได้ใช้ ZKGrails มากกว่า Grails เนื้อในแท้ๆ ซะอีก จึงจะลองเขียน tutorial ซักครั้งนึง เพื่อคนที่อยากลองใช้แต่เริ่มไม่ถูกซักที

อะไรคือ ZKGrails


ZKGrails เป็น plug-in ของ Grails ที่ทำให้ Grails เหมาะสำหรับใช้ ZK Framework (ZK เป็น AJAX Framework ตัวนึงของภาษา Java ใครอยากลองไปที่นี่ http://www.zkoss.org/) ซึ่งตอนนี้เพิ่งออกเวอร์ชั่น 2.0 ไปซึ่งมีความารถที่ใช้ง่ายขึ้น และมีหลาย feature ที่น่าสนใจเช่น jQuery ที่ทำให้เหมือนเขียน JQuery หรือ Live ที่ทำให้เราเขียนโปรแกรมแล้วดูหน้าตาของมันได้เลย โดยไม่ต้อง refresh หรือ restart server แต่อย่างใด

จะเริ่มใช้ ZKGrails อย่างไร


สร้างโปรเจค

เริ่มต้นเราจะต้องมี Grails ซะก่อนไปดาวน์โหลดได้ที่ grails.org หลังจาก set environment เสร็จเรียบร้อยแล้วก็สร้างโปรเจคขึ้นมาชื่อว่า ondemand

ติดตั้งปลั๊กอิน

หลังจากสร้าง project ขึ้นมาแล้วให้เข้าไปใน folder project เปิดด้วย text editor ขึ้นมา(อะไรก็ได้ตามถนัด)  แล้วเปิดไฟล์ BuildConfig.groovy แล้วเพิ่ม compile ":zk:2.0.4" เข้าไป


plugins {
        runtime ":hibernate:$grailsVersion"
        runtime ":jquery:1.7.2"
        runtime ":resources:1.1.6"
        build ":tomcat:$grailsVersion"
        runtime ":database-migration:1.1"
        compile ':cache:1.0.0'
        compile ":zk:2.0.4" //<< add here
}

เมื่อเพิ่มไปแล้วตอนนี้จะยังใช้ไม่ได้ให้สั่ง


$ grails refresh-dependencies

สร้าง Domain Class

ตอนนี้เราจะสร้าง Domain Class ขึ้นมา โดยใช้ชื่อ Task โดยใช้ command

$ grails create-domain-class com.github.wingyplus.todo.Task

ด้านบนนี้ grails จะสร้าง Domain Class ตาม package ที่เราใส่ไป จากนั้นให้เราใส่ property เข้าไปหนึ่ง property ชื่อว่า subject เป็น String


package com.github.wingyplus.todo

class Task {
    String subject

    static constraints = {
    }
}

สร้างหน้า zul

เราจะสร้างหน้า zul ด้วยคำสั่ง

$ grails create-zul com.github.wingyplus.todo.todo

 ไฟล์ zul ที่ได้จะอยู่ใน grails-app/zul และข้างในจะซ้อนด้วยโฟลเดอร์ตามชื่อ package เช่นไฟล์ชื่อ todo.zul ที่สร้างมาตามตัวอย่างจะอยู่ใน grails-app/zul/com/github/wingyplus/todo/ และจากผลลัพธ์ที่สั่งไปมันไม่เพียงแต่จะสร้างหน้า zul ออกมาเท่านั้นยังสร้างคลาสที่ชื่อว่า TodoComposer ซึ่งนี้ Composer จะอยู่ใน grails/app/composers ถามว่ามันคืออะไร? เจ้าตัว Composer นี้จะทำหน้าที่เป็น controller คอยควบคุมการทำงานต่างๆ ของ zul เพื่อให้ง่ายต่อการดูแล และยังให้ Class unit tests มาอีกด้วย (ไว้คราวหน้าผมจะมาพูดถึงอีกทีเรื่อง unit tests)

แก้ไข todo.zul

เราจะแก้ไข todo.zul ตามโค้ดด้านล่าง


<window apply="com.github.wingyplus.todo.TodoComposer">

        <hbox align="center">
            <image src="${z.resource(dir:'images', file:'zkpowered_s.png')}"/>
            <image src="${z.resource(dir:'images', file:'grails_logo.png')}"/>
        </hbox>
        <vlayout>
            <hbox>
                <textbox id="subject" rows="3" cols="50"/>
                <button id="createTask" label="create task"/>
            </hbox>

            <vbox id="taskList"/>
        </vlayout>

</window>

จากข้างบนเราสร้าง textbox และ button เอาไว้เพิ่ม task เมื่อกดปุ่ม create task

เพิ่ม task

หลังจากที่เราสร้าง component แล้วต่อไปเราจะสร้าง event เพื่อบันทึก task กัน โดยเปิดไฟล์ TodoComposer.groovy อยู่ใน grails-app/composers/com/github/wingyplus/todo/ ขึ้นมาแล้วเพิ่มโค้ดตามด้านล่าง


@Listen('onClick = button#createTask')
def createTask() {
        def subject = $('#subject').text()
        def task = new Task(subject: subject).save()

        addTaskToList(task)
}

ในการสร้าง event เราจะใช้ annotation ชื่อว่า Listen เพื่อบอกว่า event อะไรบ้างที่จะเข้ามาทำงานที่ method นี้โดยโค้ดข้างบนบอกว่า ให้ event onClick ของ button ที่ id ชื่อ createTask เข้ามาทำงานที่ method ชื่อว่า createTask จากนั้นก็ให้เอาข้อความจาก Textbox มา save แล้วเอา object ที่ได้จากการ save โยนไปให้ method addTaskToList แต่เราจะเห็นว่ามีของหน้าตาคุ้นๆ ละม้ายคล้าย jQuery ด้วย ซึ่งจะพูดในหัวข้อถัดไป


เมธอด addTaskToList(task)

ใน method นี้จะเอา object ที่ได้ไปสร้าง UI และ event สำหรับ UI นั้นๆ กัน

def addTaskToList(task) {
    $('#taskList').append {
        div(id: "task_${task.id}", sclass: 'task-card') {
            label(id: "task_${task.id}_done", value: "x", sclass: "task-done")
            textbox(id: "subject_${task.id}", value: task.subject, inplace: true, instant: true)
        }
    }

    $("#task_${task.id}_done").on('click', {
        $("#task_${task.id}").detach()
        task.delete()
    })

    $("#task_${task.id}").link(task, [subject: "#subject_${task.id}"])

    $("#subject_${task.id}").on('change', {
        task.save()
    })
}

ใน method นี้เราจะใช้ feature ของ ZKGrails ชื่อ jQuery API ซึ่งมีความสามารถคล้ายกับ jQuery ใน javascript


$('#taskList').append {
    div(id: "task_${task.id}", sclass: 'task-card') {
        label(id: "task_${task.id}_done", value: "x", sclass: "task-done")
        textbox(id: "subject_${task.id}", value: task.subject, inplace: true, instant: true)
    }
}

method append() ที่เอาไว้เพิ่ม component UI เข้าไปโดย ZKGrails จะใช้สิ่งที่เรียกว่า Builder ในการสร้าง UI ซึ่งกำหนด attributes ได้เหมือน zk markup ทั่วไป


$("#task_${task.id}_done").on('click', {
    $("#task_${task.id}").detach()
    task.delete()
})

method on() เอาไว้ใส่ event listener ให้กับ component นั้นๆ โดย parameter แรกที่ใส่เข้าไปคือ event ของ component นั้นและ action ที่จะเกิดขึ้นกับ event นั้นๆ ซึ่งรับเป็น closure (ใน jQuery ของ javascript รับเป็น function) จากโค้ดตัวอย่างจะบอกว่าเมื่อ component #task_done_${task.id} มีการ click ให้ทำการลบ component #task_${task.id} ออกและลบ domain object ทิ้ง


$("#task_${task.id}").link(task, [subject: "#subject_${task.id}"])

$("#subject_${task.id}").on('change', {
    task.save()
})

สุดท้ายคือ method link() เอาไว้ทำ databinding ผูกกับ object โดย parameter ตัวแรกรับเป็น object ที่จะทำ databinding และ parameter ตัวที่สองเป็น map บอกว่า จะให้  properties ของ object ผูกกับ component อะไร เมื่อมี event ที่ต้องกระทำกับ object นั้นก็สามารถนำไปใช้ได้เลย จากตัวอย่างจะเห็นว่าเราผูกกับ object ชื่อว่า task โดยให้ property ชื่อ subject ผูกกับ component id ชื่อ #subject_${task.id}

Start app

หลังจากนี้จะทำการ start app ขึ้นมาใช้กัน โดยสั่ง

$ grails run-app 

หลังจากนั้นไปที่ url http://localhost:8080/todo/com/github/wingyplus/todo/todo.zul

สุดท้าย


ตอนนี้ feature ของ ZKGrails ก็มีออกมาให้เห็นเรื่อยๆ ถ้ามีอะไรใหม่ผมก็จะเอามาเขียนลง blog ต่อไป ในบทต่อไปผมก็คิดว่าคงจะพูดเรื่อง test กับ ZKGrails กันว่าเราจะเทสมันยังไงดี :)

ใครอยากได้ source code ไปเอาได้ที่ https://github.com/wingyplus/zodo นะครับ บ๊ายบาย นอนหลับฝันดี