Tuesday, July 10, 2007

ทดลอง implement widget บน GWT

ช่วงนี้ผมกำลังทดลองเขียนโปรแกรม hangman
เพื่อจะได้ทดสอบ framework ต่างๆเปรียบเทียบกัน
โดยต้นแบบของเกมส์ ได้มาจาก code ตัวอย่างใน cincom smalltalk
ที่ใช้ seaside เป็น framework

บน game Hangman, user สามารถกดเลือกตัวอักษรที่จะทายได้
โดยตัวอักษรพวกนี้ จะ render เป็น hyperlink ไว้
และเมื่อ user กดเลือกไปแล้ว, hyperlink นั้นก็จะกลายเป็น text ธรรมดา
เพื่อเป็น feedback ให้ user รู้ว่าได้ทำการเลือกไปแล้ว



ผมลอง implement feature ที่ว่าเป็น widget ใน GWT ดู
เกริ่นก่อนว่า, ใน GWT ก็มี widget ประเภท hyperlink ให้ใช้
แต่มีข้อจำกัดว่า ไม่สามารถทำให้มันกลายเป็น text ธรรมดาได้
เราก็เลยต้อง customize ทำ hyperlink แบบพิเศษของเราขึ้นมาเอง

ทางเลือกในการ implement hyperlink แบบพิเศษของเรา มีอยู่ 2 ทางคือ
1. extends จาก hyperlink ของเดิม แล้ว overide method บางตัว เพื่อปรับพฤติกรรม
2. เขียนขึ้นมาเอง โดยไม่ยุ่งกับ HyperLink widgetเลย
ผมเลือกใช้วิธีที่ 2 เพื่อจะได้ทำความเข้าใจกับกลไกการทำงานของ GWT มากขึ้น

เริ่มแรกสุด ก็คือเลือก extend widget ของเราจาก FocusWidget,
โดย FocusWidget คือ base class สำหรับ widget ที่สามารถ click
หรือเรียกใช้ผ่าน access key ได้
public class Letter extends FocusWidget {

}

ขั้นถัดมา ก็คือ implement ส่วนที่เกี่ยวกับ การ render
วิธีการที่ GWT ใช้ render widget ก็คือ render เป็น DOM element
public class Letter extends Widget {
private Element anchorElem;
private char ch;

public Letter(char ch) {
this.ch = ch;
setElement(DOM.createSpan());
DOM.appendChild(getElement(), anchorElem = DOM.createAnchor());
DOM.setInnerText(anchorElem, Character.toString(ch));
DOM.setElementProperty(anchorElem, "href", "#");
}
}

ผลลัพท์ที่ได้จากข้างบน ก็คือเราจะได้ html element ดังนี้
<span><a href="#"></a></span>


ข้อสงสัยก็คือ ถ้า render แค่นี้แล้ว GWT จะรับ javascript event onclick ได้อย่างไร
คำตอบก็คือ ถ้าเราไปไล่ code ของ FocusWidget ดู
เราจะเห็นว่าหลังจากที่เราสั่ง setElement
code ใน FocusWidget จะทำการเรียกใช้
sinkEvents(Event.ONCLICK | Event.FOCUSEVENTS | Event.KEYEVENTS);

ซึ่งเป็นการระบุว่า widget เรา สามารถรับ event กลุ่ม click, focus, keyevent ได้
โดย callback ที่ต้อง implement เพื่อรับ event ก็คือ method onBrowserEvent
ในกรณีของเรา FocusWidget มีการ implement onBrowserEvent ไว้ดังนี้แล้ว
public void onBrowserEvent(Event event) {

switch (DOM.eventGetType(event)) {
case Event.ONCLICK:
if (clickListeners != null) {
clickListeners.fireClick(this);
}
break;
...
}
}

สิ่งที่เราต้องการ overide ก็คือ เมื่อเกิด clickEvent แล้ว
เราต้องการให้ hyperlink ของเรา click อีกไม่ได้
ซึ่งทำง่ายๆโดย เอา attribute href ออกจาก anchor tag
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
if (DOM.eventGetType(event) == Event.ONCLICK) {
DOM.removeElementAttribute(anchorElem, "href");
DOM.eventPreventDefault(event);
}
}


เมื่อได้ตัวอักษรเดี่ยวๆแล้ว ก็มาลองทำ widget ที่ช่วย render ชุดตัวอักษรบ้าง
โดย widget นี้เราจะนำเอา Letter widget ที่เราพึ่งทำไปมาเรียงต่อๆกัน
ในกรณีนี้ widget ที่เราจะเขียน จะ extend จาก Composite
public class Letters extends Composite implements ClickListener {

private Letter[] letters;

public Letters(String chars) {
FlowPanel panel = new FlowPanel();
char[] chs = chars.toCharArray();
letters = new Letter[chs.length];
for (int i = 0; i < chs.length; i++) {
letters[i] = new Letter(chs[i]);
letters[i].addClickListener(this);
panel.add(letters[i]);
panel.add(new Space(1));
}
initWidget(panel);
}

...

}


ความรู้สึกที่ได้จากการทดลอง implement widget ใน GWT ก็คือ
1. ความรู้สึกก้ำกึ่ง ระหว่างความชอบ กับ ไม่ชอบ
เนื่องจาก บางอย่าง ถ้าเราเขียนด้วย javascript โดยตรง มันจะง่ายกว่า
แต่บางอย่าง เขียนด้วย GWT ก็จะง่ายกว่า (เช่น sinkEvent)
2. GWT มันไม่ support feature ของ Java 1.5
ทำให้รู้สึกเย่นเย้อเวลาเขียนบางอย่าง เช่นพวก for loop
หรือต้องใส่ casting เวลา access collection

Related link from Roti

10 comments:

veer said...

เพื่อนบ่นๆ ว่า restart นาน (ผมเลยอยากได้เครื่องแรงๆ :-P)

veer said...

GWT เอามาใช้กับ SVG ได้หรือเปล่าครับ?

PPhetra said...

ไม่รู้ว่าเวลานี่ขึ้นกับขนาดด้วยหรือเปล่านะ
ของผม code ยังเล็กอยู่ เลยไม่รู้สึก

restart นานๆ ก็มีข้อดีและข้อเสียนะ
ข้อเสีย ก็คือ สำหรับ programmer ที่เป็น style
แก้นิด run อีกสักหน่อย, จะเสียเวลามาก

ส่วนข้อดี ก็คือ ทำให้เราคิดรอบคอบและทบทวนในใจมากขึ้น

(Note: ถ้าเป็นการเรียนรู้เรื่องใหม่ ผมสนับสนุน พวก feedback เร็วๆ)

PPhetra said...

น่าลอง implement รูป hangman ด้วย svg เนอะ
http://roberthanson.blogspot.com/2006/06/coding-svg-with-gwt.html

Anonymous said...

ตอนนี้ผมใช้ GWT พัฒนา webapp ตัวนึงอยู่ แล้วก็รู้สึกดีกับมันทีเดียวเลยล่ะ ชอบ มันช่วยลดความยุ่งยากซับซ้อนไปได้หลายอย่าง แล้วผมก็เป็นสาย Java Programmer อยู่แล้วด้วย เลยไม่ต้องเสียเวลาเรียนรู้อะไรมาก

เรื่องการ support Java 1.5 features ที่คุณ pok พูดถึงนี่ ส่วนตัวผมเฉยๆนะ ไม่รู้สึกว่าเป็นข้อเสียอะไร :-)

PPhetra said...

panuta: ใช่ๆ ใช้ java1.5 ไม่ได้ ไม่ใช่ข้อเสียครับ
แต่เป็นเพราะหลังๆผมใช้ ภาษาอื่นๆที่มัน express ได้สั้นกระทัดรัดกว่า ก็เลยรู้สึกว่า "เฮ้อทำไมต้องเขียนยาวจัง"

PPhetra said...

แวบไปดูใน issues list ของ GWT มา
http://code.google.com/p/google-web-toolkit/issues/list?q=label:Milestone-Planned

ดูแล้ว version หน้า คงจะ support generic​​ แล้ว

Anonymous said...

พี่ป๊อกไม่ลองเอา ofbiz framework มาทำดูบ้างเหรอครับน่าจะสนุก

Anonymous said...

gwt ใช้กับ SVG ได้นะครับ แต่ผมยังไม่เคยลอง สนใจดูได้ ที่ http://roberthanson.blogspot.com/2006/06/coding-svg-with-gwt.html

Satsaid said...

เยี่ยมครับ ชอ bookmark ไว้ก่อนเดี๋ยวจะกลับมาศึกษาใหม่ ชอบครับ blog ผมเต็มไปหมดเลย แต่ไม่ได้ทำเอง อยากทำเองเหมือนกัน

http://satsaid.blogspot.com