ที่ออกแบบมาเพื่อใช้จัดการเรื่องการ submit ข้อมูลผ่าน html form
CForms มีองค์ประกอบ 2 ส่วนคือ
- Form Model คือ definition ของ data ที่จะ submit เข้ามา
- Form Template คือ layout ของ widget
ที่จะสร้างเป็นหน้าจอให้ผู้ใช้เห็น
แน่นอนว่าด้วยชื่อเสียงของ Cocoon ที่เป็น Framework แห่ง xml
ทั้ง form model และ template ก็ต้อง describe ด้วย xml
สำหรับตัว Form model สิ่งที่ผมชอบก็คือ
เรากำหนด definition ของ form ผ่านทาง xml อย่างเดียว
ไม่จำเป็นต้องไปเขียน java class ใดๆเลย
ลองดูตัวอย่าง form model
<fd:form
xmlns:fd="http://apache.org/cocoon/forms/1.0#definition">
<fd:widgets>
<fd:field id="name" required="true">
<fd:label>Name:</fd:label>
<fd:datatype base="string"/>
<fd:validation>
<fd:length min="2"/>
</fd:validation>
</fd:field>
<fd:field id="email" required="true">
<fd:label>Email address:</fd:label>
<fd:datatype base="string"/>
<fd:validation>
<fd:email/>
</fd:validation>
</fd:field>
</fd:widgets>
</fd:form>
จะเห็นว่าใน form หนึ่งๆ ประกอบด้วย widget หลายๆอัน
ในตัวอย่างของเราชนิดของ widget ที่ใช้ก็คือ Field widget
โดยแต่ละ field ก็จะมีการกำหนด label, datatype, validation
(CForms provide build-in widget มาให้พอสมควร
และเปิดช่องให้เรา define custom widget เข้าไปได้)
ส่วนหน้าตาของ form template ก็จะมีหน้าตาแบบนี้
<html xmlns:ft="http://apache.org/cocoon/forms/1.0#template"
xmlns:fi="http://apache.org/cocoon/forms/1.0#instance"
xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
<jx:import uri="resource://org/apache/cocoon/forms/generation/jx-macros.xml"/>
<head>
<title>Registration form</title>
</head>
<body>
<h1>Registration</h1>
<ft:form-template action="#{$continuation/id}.continue" method="POST">
<ft:widget-label id="name"/>
<ft:widget id="name"/>
<br/>
<ft:widget-label id="email"/>
<ft:widget id="email"/>
<input type="submit"/>
</ft:form-template>
</body>
</html>
จะเห็นว่ามีการ map widget เข้ากับบริเวณต่างๆใน template
เมื่อมีการ render page นี้, component ย่อยแต่ละตัว ก็จะรับผิดชอบ render ตัวเอง
แล้วนำผลลัพท์ที่ได้มาแทนที่ลงไป
และเมื่อ form ถูก submit กลับเข้ามา,
CForms ก็จะแจกให้ component แต่ละตัว
รับผิดชอบในการดึงข้อมูลออกจาก HttpRequest
และนำข้อมูลท่ี่ได้ไปใส่ไว้ใน form instance
เวลาจะนำ CForm ไปใช้ เรามักจะใช้ผ่าน flowscript
ลองดูตัวอย่าง
cocoon.load("resource://org/apache/cocoon/forms/flow/javascript/Form.js");
function registration() {
var form = new Form("registration_definition.xml");
form.showForm("registration-display-pipeline");
var viewData = { "username" : form.getChild("name").getValue() }
cocoon.sendPage("registration-success-pipeline", viewData);
}
โปรแกรมตัวอย่างนี้ จะแสดงหน้าจอให้ user กรอก และเมื่อ user submit เข้ามา
ก็จะทำการแสดงหน้า success
จุดเด่นของ flowscript ก็คือ
1. ใช้ syntax javascript ซึ่ง implement ได้เร็วกว่า java code
2. implement โดยใช้ continuation ทำให้เขียน code อยู่เป็นกลุ่มก้อน ง่ายต่อการ maintain
framework ปัจจุบันที่ใช้ continuation style แบบนี้ ก็คือ Seaside ที่ใช้ภาษา smalltalk,
แล้วก็ web framework ของ DrScheme ที่เขียนด้วยภาษา scheme,
ส่วนในค่าย java นี้ก็มีแค่ cocoon ตัวเดียว
(่jetty continuations เป็นอีกเรื่องหนึ่ง ที่ใช้ศัทพ์เดียวกัน แต่คนละ concept)
ในโปรแกรมข้างบน จุดที่เกิด continuation ก็คือ method form.showForm
ซึ่ง cocoon จะทำการ pause execution ไว้ก่อน
เมื่อ user submit เข้ามา จึงจะเริ่มทำงานต่อจากจุดเดิม
ทั้งหมดข้างบน เป็นแค่ demo การใช้ง่านง่ายๆ
แต่เวลานำไปใช้จริงๆ ข้อมูลนำมาใช้บน form
มักจะอยู่ในรูป xml หรือ beans
ดังนั้นจึงต้องมีขบวนการ binding เกิดขึ้นด้วย
CForms ใช้ XPath เข้ามาช่วยในการ binding
ลองดูตัวอย่าง
<fb:context xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" path="/" >
<fb:value id="name" path="name"/>
<fb:value id="email" path="email"/>
</fb:context>
attribute path ข้างบน ก็คือ xpath ที่ชี้ไปยัง xml element ที่ต้องการ bind
ส่วนประเด็นสมัยนิยม ก็คือเรื่อง Ajax
ตัว CForms ใช้หลักการ partial renderer
ดังนั้นในตอนที่เราเขียน template ก็ต้องใช้พวก tag พวก div, span
เข้ามา grouping บริเวณที่จะเกิดการ replace
ส่วนการ handler request
เนื่องจาก cocoon เป็นเจ้าพ่อแห่ง pipeline
ดังนั้นเราก็แค่แทรก tranformer กับ selector ที่ใช้ handle ajax
เข้าไปใน sitemap
เท่าที่ดูมา CForms ก็ดูเข้าท่าดีเหมือนกัน
ประเด็นที่ต่างกับพวก Tapestry หรือ JSF ก็คือ
- ใน Tapestry form จะปนๆอยู่กับ controller
แต่ใน CForms, Forms จะแยกออกจาก controller อย่างชัดเจน
(seperation of concern จะดีกว่า) - ใน JSF เวลาจะเลือกประเภทการ render เราทำได้โดยเปลี่ยน RenderKit
ใน CForms เนื่องจากเราใช้ concept ของการ transform
ดังนั้นการเปลี่ยนการ render ก็คือการเปลี่ยน XLST
ส่วนใน Tapestry ไม่มี concept แบบนี้ - สามารถใช้ได้โดยไม่ต้องรู้ java เลย
ส่วนข้อเสียที่เห็นได้ชัดก็คือ XML เยอะจริงๆ