วันอาทิตย์ที่ 27 มีนาคม พ.ศ. 2559

Vaadin: How to listen to animation end part 2

>> มาสร้างโปรเจ็กต์แรกกันเถอะ ด้วย netbeans เวอร์ชัน 8.1 กับ jdk เวอร์ชัน 1.8.0_65

1) เลือกสร้างโปรเจ็กต์จาก maven ด้วยวิธีการ project from archetype

2) ที่หน้าต่าง maven archetype ให้เพื่อนๆค้นหาแม่แบบที่เป็น vaadin-archetype-application (อ่านเพิ่มเติมสำหรับแม่แบบอื่นๆ)

3) ตั้งชื่อโปรเจ็กต์, กำหนดที่อยู่ของโปรเจ็กต์, ตั้งชื่อ group id รวมไปถึงรายละเอียดปลีกย่อยด้านล่าง ได้แก่ ชื่อของ widgetset และชื่อของคลาสหลัก

4) เสร็จแล้วโปรดคอยสักครู่ เจ้า maven นี้จะตามไปโหลดไฟล์จาร์ที่เกี่ยวข้องกับแม่แบบที่เราได้เลือกไว้มาให้ และหลังจากนั้นก็เปิดเข้าไปดูหน้าตาของ source code ที่มันเขียนไว้ให้กันเลย

5) เดี๋ยวผมจึงจะอธิบายว่ามันเขียนอะไรมาให้บ้าง ตอนนี้อยากให้ลองรันโปรเจ็กตนี้กันก่อนครับ ด้วยการคลิกขวาที่ชื่อโปรเจ็กต์แล้วเลือก run มันจะถามว่าจะรันบน server ตัวไหน ผมเลือกเป็น glassfish server เวอร์ชัน 4.1.1 จากนั้นกดปุ่ม ok

6) เข้าใจว่ามันจะ compile สิ่งที่เรียกว่า widgetset ด้วย (ถ้ามี) รวมไปถึงสร้าง .war และ deploy ขึ้นสู่ server

7) และปรากฏสู่สายตา


>> มาทำความเข้าใจโค้ดกันเถอะ นี่ก็เพื่อมิตรอันดีระหว่างผมกับผู้เริ่มต้นใหม่ที่ใช้ netbeans มาสร้างโปรเจ็กต์ตามผม ทว่าโพสต์นี้ก็ไม่ได้สอนพื้นฐานเพื่อที่จะเข้าใจ vaadin หรอกนะครับ หลังจากนี้ผมจะมุ่งไปที่การจับเหตุการณ์เมื่อ animation เล่นจบแล้วหรือก็คือตามวัตถุประสงค์ของโพสต์นี้นั่นเอง

- บรรทัดที่ 24 บอกว่าเราใช้ theme ชื่อ mytheme ซึ่งอยู่ ณ end_animator\src\main\webapp\VAADIN\themes
- บรรทัดที่ 25 บอกว่าเรามีไฟล์ .gwt.xml หรือก็คือ widgetset อยู่ที่ end_animator\src\main\resources\com\pros\endanimator


- บรรทัดที่ 29 นี่เป็นเมธอดแรกที่ vaadin จะเข้ามาทำงาน
- บรรทัดที่ 30 คือ layout แบบ vertical เหมือนกับจานที่ตั้งซ้อนกันขึ้นไป เพียงแต่ความจริงแล้วการเพิ่ม component จะเป็นการเรียงลงมาด้านล่าง หากแกะดู html code ก็จะพบว่ามันคือแท็ก div ที่วางเรียงต่อกันจากบนลงล่างครับ
- บรรทัดที่ 32 สร้างช่องรับข้อมูลหรือก็คือ text field ขึ้นมา จากนั้นระบุ caption ว่า Type your name here:
- บรรทัดที่ 35 สร้างปุ่มหรือก็คือ button โดยระบุ caption ว่า Click Me จากนั้นกำหนด click event ให้กับปุ่ม ประมาณว่ากดปุ่มแล้วอยากจะให้ทำอะไรด้วยวิธีการเขียนของจาวา 1.8 ที่เรียกว่า lambda expressions (อ่านเพิ่มเติม) ซึ่งต้องการให้เพิ่ม label เข้าไปใหม่ทุกครั้งที่กดปุ่ม โดยให้ label นี้มีค่าเป็น Thanks ตามด้วยข้อความที่เราได้กรอกเข้าไปในช่อง text field ตามด้วย , it works!
- บรรทัดที่ 41 เพิ่ม text field กับ button เข้าไปใน layout
- บรรทัดที่ 42 กำหนด padding ใหักับ layout (ระบบ margin ของ vaadin ก็คือ padding ครับ)
- บรรทัดที่ 43 กำหนดพื้นที่ว่างระหว่าง text field กับ button ให้แลดูสวยงาม
- สุดท้ายบรรทัดที่ 45 ก็กำหนด layout ให้กับ panel หลักหรือก็คือพื้นที่ที่ใช้แสดงผลนั่นเอง (แท็ก body) หากขาดบรรทัดนี้ไป เราก็จะมองไม่เห็นอะไรเลย (เห็นหน้าเปล่าๆ)


- บรรทัดที่ 48 กำหนดให้มี servlet (อย่างที่เพื่อนๆทราบ พื้นฐานของจาวาเว็บก็คือ servlet) ตัวหนึ่งมีหน้าที่รับผิดชอบต่อประดา request ที่ถูกยิงเข้ามาหาโปรเจ็กต์นี้ (สำหรับ /* คือทุก request)
- บรรทัดที่ 49 กำหนดให้ MyUI เป็นคลาสหลักที่สืบทอดจากคลาส com.vaadin.ui.UI (vaadin ตั้งแต่เวอร์ชัน 7 เป็นต้นมาคลาสหลักต้องเป็นลูกของคลาส UI) ทั้งกำหนดโปรเจ็กต์นี้ถูกจัดการหน่วยความจำแบบ dev mode (หรือก็คือการกำหนดว่า production mode มีค่าเป็น false)
- บรรทัดที่ 50 พูดถึงบรรทัดที่ 48 เจ้า servlet ที่รับผิดชอบต่อทุก request ของโปรเจ็กต์นี้ก็คือคลาสชื่อ MyUIServlet ที่เป็นลูกของ VaadinServlet นั่นเองครับ

>> ที่กล่าวมานี้ก็เพื่อมือใหม่ที่หัดใช้ netbeans มาทำงาน vaadin ว่าอย่างน้อยๆเราก็เป็นมือใหม่หัดใช้เหมือนกัน (โพสต์นี้ real time เลยก็ว่าได้) และต่อไปผมขอนำเข้าสู่เนื้อหาแท้จริงที่ต้องการนำเสนอเลยนะครับ

>> ผมต้องการสร้าง widgetset เพื่อทำงาน javascript กับ rpc, ผมพบอยู่ 2 วิธีครับ
1) สร้างทีละคลาสเอง (= =) อื่ม... ก็ทำได้อยู่ หรือ
2) ติดตั้ง vaadin plugin ตามขั้นตอนต่อไปนี้ (ผมเลือกวิธีนี้นะ)
จาก https://www.youtube.com/watch?v=P9YupaOpsMk
- เลือกเมนู Tools ตามด้วย Plugins

- เลือกแท็บ Available Plugins และค้นหาด้วยคีย์ว่า vaadin

- พบ plugin ชื่อดังรูปด้านล่างนี้ (เวอร์ชันอาจต่างกัน) ก็ติดตั้งเลยครับ


- เมื่อติดตั้งเสร็จเรียบร้อยแล้ว (รวมถึงหลังจากที่เจ้า netbeans ขอ restart) เพื่อนๆจะสามารถสร้างโปรเจ็กต์ vaadin ได้ โดย netbeans รู้จักวิธีการดังกล่าวแสดงอยู่ในช่อง Categories และมีรูปแบบย่อยให้เลือกสร้างในช่อง Projects (ทดลองสร้างโปรเจ็กต์ใหม่อีกสักรอบ ต้องเห็นดังรูปด้านล่างนี้)


>> ทีนี้เรากก็ขอให้ netbeans สร้าง widgetset ให้ได้แล้วล่ะ คลิกขวาที่โปรเจ็กต์เลย แล้วเลือก New > Other... > มองหา Vaadin แล้วเลือก File Types เป็น Vaadin Widget กดปุ่ม Next

- ผมตั้งชื่อ Class Name: เป็น EndAnimator และคลาสนี้อยู่ฝั่งหลังบ้านหรือก็คือ vaadin
- ให้สังกัด Package: เป็น com.pros.endanimator
- ตัวเลือกด้านล่าง เลืก Server side component / Client side connector with shared state and RPC, including custom widget. เดี๋ยวอันไหนไม่ได้ใช้ค่อยลบออก
- กด Next เลย

- โดยปกติแล้ว หลังบ้าน (vaadin) กับ หน้าบ้าน (gwt) จะติดต่อกันผ่าน interface ที่ระบบนี้สามารถเข้าใจกันได้ และโดยปกติแล้วหลังบ้านจะใช้ com.vaadin.ui.AbstractComponent interface แต่ครั้งนี้ผมขอเปลี่ยนเป็น com.vaadin.server.AbstractExtension ทว่าไม่มีให้เลือก ? โอเค งั้นก็ใช้ com.vaadin.ui.AbstractComponent ไปก่อน เดี๋ยวค่อยไปแก้ไขเอาทีหลัง กดปุ่ม Finish

- netbeans ก็จะสร้างคลาสและอินเตอร์เฟสต่างๆออกมา อย่างที่ผมได้รับตามนี้ครับ


>> เป็นว่าตอนนี้ผมให้ netbeans เตรียม widgetset ให้เสร็จแล้ว แต่มันไม่ได้ทำงานตามที่ผมต้องการ แต่ผมก็อยากจะอธิบายเสียหน่อยว่า widget ใหม่ที่เพิ่งได้มานี้ เขาเขียนโค้ดให้มันทำงานอะไร เรามาดูแต่ละคลาสแต่ละอินเตอร์เฟสที่สำคัญกันก่อนนะ ซึ่งแบ่งออกเป็นสองฝั่ง
- ฝั่งหลังบ้าน ตอนนี้มีคลาส EndAnimator กับ MyUI
- ฝั่งหน้าบ้าน คือทั้งหมดใน package ชื่อ com.pros.endanimator.client นั่นแหละ ประกอบด้วย EndAnimatorClientRpc, EndAnimatorConnector, EndAnimatorServerRpc, EndAnimatorState และ EndAnimatorWidget

>> คลาส EndAnimator (หลังบ้าน) มี constructor ทำงาน register ออบเจ็กต์ rpc หลังบ้านไว้ตัวหนึ่ง พร้อมกับบริการ getState ที่สามารถให้ค่าของตัวแปรชื่อ text ออกมาได้, มองไปที่ตัวออบเจ็กต์ rpc มันคือ EndAnimatorServerRpc ที่เป็น interface โดยจะรอสัญญาณ (จากหน้าบ้าน) เพื่อทำงานเมธอด clicked และมี MouseEventDetails เป็นพารามิเตอร์ (อ่านทั้งหมดนี้ด้วยตัวเอง) ดูรูปประกอบ

- หมายความว่าอย่างไรสำหรับสิ่งที่ออบเจ็กต์ rpc นี้ทำ หมายความว่า ทุกครั้งที่เมธอด clicked นี้ถูกเรียก ค่าของตัวแปร clickCount จะเพิ่มขึ้นหนึ่งทุกครั้งไป และเมื่อไรก็ตามที่ค่าของมันเอาไปหารเอาเศษด้วย 5 แล้วได้ค่าเป็น 0 มันจะเรียกไปที่ rpc อีกตัวที่อยู่ฝั่งหน้าบ้าน (ซึ่งตอนนี้เรายังไม่รู้จัก) โดยบอกให้ rpc ฝั่งหน้าบ้านนี้ทำงานเมธอด alert แสดงข้อความ Ok, that's enough! ออกมา
- โดยทุกรอบที่เมธอด clicked นี้ทำงานก็จะเปลี่ยนค่าตัวแปร text ที่ถูกสร้างไว้ในคลาส EndAnimatorState เป็น You have clicked ตามด้วยค่า clickCount และตามด้วย times (ประมาณว่านับครั้งให้)
- เพื่อนๆมือใหม่จะเห็นว่าคลาส EndAnimator นี้ไม่มีหน้าตาใดๆเลย มันไปอยู่ไหนนะ?

>> คลาส EndAnimatorConnector (หน้าบ้าน) มี constructor ทำงาน register ออบเจ็กต์ rpc หน้าบ้านไว้ตัวหนึ่ง, บริการ getState ที่สามารถให้ค่าของตัวแปรชื่อ text ออกมาได้, บริการ getWidget คือช่องทางที่ connector จะสื่อสารกับ gwt, กลไก onStateChanged ที่ทำงานอัตโนมัติเมื่อมีการเปลี่ยนค่าของตัวแปร text จากหลังบ้านมายังหน้าบ้าน (ปกติแล้ว vaadin สามารถคุยกับ gwt ได้ก็ผ่าน onStateChanged นี้ครับ แต่ถ้า gwt จะคุยกลับไปที่ vaadin จะต้องใช้ rpc มาช่วย), มองไปที่ตัวออบเจ็กต์ rpc มันคือ EndAnimatorClientRpc ที่เป็น interface โดยจะรอสัญญาณ (จากหลังบ้าน) เพื่อทำงานเมธอด alert และก็จะใช้ javascript เขียน message แบบกล่องข้อความออกไป ซึ่งก็คือข้อความว่า Ok, that's enough! นั่นเอง

- คำถามคือ แล้วเมื่อไรที่ rpc ฝั่งหลังบ้านจะถูกเรียกล่ะ? เพราะเท่าที่อธิบายมา ดูเหมือนว่า logic นี้จะคอยให้มีการส่งสัญญาณไปหา rpc ฝั่งหลังบ้านก่อน จากนั้นฝั่งหน้าบ้านจึงจะ alert ข้อความออกมาได้ ฮั่นแน่!เพื่อนๆที่ตาไวตอบผมได้ทันควัน บรรทัดที่ 33 ในรูปข้างต้นนั่นไงที่จะผลิตสัญญาณหรือก็คือการเรียกเมธอด clicked ของฝั่งหลังบ้านให้ทำงานตาม logic นี้โดยส่งพารามิเตอร์ที่มันต้องการให้ด้วย นั่นคือออบเจ็กต์ MouseEventDetails ครับ
- เรื่องของเรื่องจะเริ่มจากผู้ใช้ต้องเห็นหน้าตาของ widget ที่ browser ก่อนจากนั้นทำอะไรกับมันสักอย่าง ฮั่นแน่!เพื่อนตาไวคนเดิมตอบผมทันทีว่า คลิก ผู้ใช้คลิกอะไรสักอย่างไง! ใช่ครับบรรทัดที่ 28 เราได้ขอ widget จาก getWidget เพื่อคอยฟัง ClickHandler (ฟังการคลิกจากผู้ใช้) เพราะเมื่อคลิกเข้ามา onClick จะทำงาน เกิดการสร้างออบเจ็กต์ MouseEventDetails ด้วยกลวิธีดังในรูป แล้วส่งให้กับ rpc หลังบ้านทำงานในที่สุด เย้! เป็นว่าเพื่อนๆกับผมเข้าใจหัวใจการทำงานของระบบ rpc นี้แล้วนะครับ

>> สุดท้ายที่จะอธิบายก็คือคลาส EndAnimatorWidget หรือก็คือ widget เจ้ากรรมที่ผู้ใช้จะมองเห็นหน้าตาของมันนั่นเอง คลาสนี้เป็นลูกของ com.google.gwt.user.client.ui.Label ดังนั้นหน้าตาที่จะเห็นก็ต้องเป็น label ที่ถูกกำหนดค่ามาให้

ฮั่นแน่! เพื่อนตาไวอีกคนรู้เลยว่าค่าของมันก็คือ
"You have clicked " + clickCount + " times"
ที่ถูกกำหนดมาตั้งแต่แรกที่คลาสหลังบ้าน EndAnimator นั่นแล้ว (ลองกลับไปดูบรรทัดที่ 21 ของคลาส EndAnimator) แต่ผิดครับ ไม่ใช่ข้อความ "You have..." มันคือข้อความว่า "This is EndAnimator" นั่นเพราะว่ามันคือค่าแรกที่กำหนดให้กับตัวแปร text ในคลาส EndAnimatorState (ลองเปิดดูสิ) ถือว่าเป็นเสน่ห์ของ EndAnimatorState ที่สามารถส่งค่าของตัวแปรใดๆ (ต้องมีชนิดตามที่ระบบของทั้งสองฝั่งรู้จักนะ) ออกไปได้ผ่านบริการ getState ของทั้งฝั่งหลังบ้านและหน้าบ้านครับ

>> เอาล่ะ เรามาทดลองรันมันกันเถอะ โดยกลับไปแก้ไขคลาส MyUI ให้เรียกมันออกมาก่อน ตามนี้

- เพื่อนๆควรทราบว่า หากเราได้เคยลองรันมาแล้วแล้วรันใหม่ ผลลัพธ์ที่ได้จะไม่เปลี่ยนแปลง เนื่องจากว่า server ยังคงใช้ .war อันเก่า เราต้องหาวิธีการให้ server มันรู้ว่าต้องใช้ .war ใหม่นะ (เพราะ source code เราได้เปลี่ยนแปลงไปแล้ว)
- ให้คลิกขวาที่โปรเจ็กต์ เลือก Build หรือ Clean and Build เพื่อ compile ทุกสิ่งใหม่ จากนั้นจึงคลิกขวาที่โปรเจ็กต์อีกครั้งแล้วสั่ง run ก็จะเป็นการ deploy .war ใหม่ขึ้นสู่ server ครับ (ใครมีวิธีดีกว่านี้ช่วยบอกผมหน่อยก็ได้นะ)

- ทดสอบคลิกไปที่ข้อความ

- และคลิกครั้งที่ 5 ซึ่งหารเอาเศษกับ 5 จะมีค่าเท่ากับ 0


>> อ๊! อธิบายเพลินจนลืมตัว มันยาวอีกแล้ว (= =) ไว้ต่อ part หน้านะครับ ดึกแล้วฝันดี

ไม่มีความคิดเห็น:

แสดงความคิดเห็น