บทสุดท้าย ในซีรีย์ Let’s Angular

หลังจากทุกๆตอนที่ผ่านมา ทำให้เราเห็นวิธีการสร้าง module, component และ service มากันอย่างครบถ้วนแล้ว และหัวข้อสุดท้าย ที่แทบจะทุกเวปต้องการ การติดต่อเพื่อดึงข้อมูลผ่าน http

ปัจจุบัน เวปไซท์เริ่มเข้าสู่การเป็น dynamic contents มากขึ้น หมายความว่า แต่ละครั้งที่ผู้ใช้เข้าถึงเวปไซท์อาจจะเห็นข้อมูลที่แตกต่างกัน ตัวอย่างเช่น facebook.com เข้าต่างเวลา ก็จะเห็น timeline ต่างออกไป ส่วนสำคัญที่ทำให้เกิด dynamic contents ก็คือ api หรือ การดึงข้อมูลผ่าน http

ในเนื้อหาตอนนี้ จะค่อยๆเปลี่ยนแปลง HeroService ที่ดึงข้อมูล static จาก HEROS.ts ที่เราจำลองเอาไว้ ไปเป็นการดึงข้อมูลผ่าน http

Register for HTTP services

ความจริงแล้ว HttpModule ไม่ได้เป็น core module ใน angular2+ อีกต่อไป แต่จะอยู่ใน add-on ที่ชื่อว่า “@angular/http” เพื่อใช้งานโมดูลนี้ เราจึงต้อง include มาเสียก่อน แต่ในโปรเจคนี้ได้ include add-on นี้มาให้เป็นที่เรียบร้อยแล้ว 🙂

เพื่อเปิดใช้งาน module เริ่มต้นจากการ import เข้าไปที่ AppModule

สำหรับคนที่เขียน server-side ได้ ในจุดนี้ก็น่าจะไม่ใช่เรื่องยากที่จะลงมือเขียน Hero Api เพื่อดึงค่าข้อมูล

สำหรับคนที่ไม่อยากสร้าง Hero Api ด้วยตัวเอง

แต่สำหรับอีกหลายๆคนที่พึ่งเริ่มเขียน อาจจะยากลำบากเล็กน้อย เพราะในบล๊อคนี้ไม่ได้พูดครอบคลุมไปถึงจุดๆนั้น แต่ไม่มีปัญหา เพราะว่า angular เสนอแนวทางเพื่อทำให้เราสามารถทดสอบ http request ได้โดยไม่จำเป็นต้องสร้าง package จริงๆ

Simulate the web API

เป้าหมายของเรา คือ

* เขียนโค้ดเพื่อสร้าง Http requests และทดสอบ

* ไม่อยากสร้าง request ไปยัง backend จริงๆ

ดูเหมือนว่าความต้องการทั้ง 2 มันจะขัดแย้งกันเอง แต่จริงๆแล้วมันก็เป็นไปได้ แค่เราเปลี่ยน core module XHRBackend ที่ควบคุมการสร้าง Http requests ได้ เราก็สามารถทำทุกอย่างที่เราต้องการได้ โดยไม่ต้องสร้าง request จริงๆ

แต่การจะเปลี่ยน core module ก็ไม่ได้เป็นเรื่องง่ายๆที่จะสอนจบภายใน 1 บล๊อค angular จึงเสนอแนวทางอีกหนึ่งแนวทาง ซึ่งก็คือ โดยใช้ InMemoryWebApiModule เราไม่ต้องรู้ว่าภายใน module มีการทำงานยังไง แต่อย่างน้อยมันจะช่วยทำให้เราสามารถเขียน Http request ได้โดยไม่ต้องอาศัย backend เยี่ยมมม ~

เริ่มต้นด้วยการ include InMemoryWebApiModule และ InMemoryDataService

* ต้องใส่ imports:[…] ตามลำดับด้วย เดี๋ยวจะรันไม่ได้

จากนั้นก็สร้าง InMemoryDataService

** โดยคร่าวๆแล้วหลักการทำงาน คือ InMemoryDataService ทำหน้าที่เป็น in-memory database ทำให้มันสามารถเก็บ/แก้ไข/เพิ่ม ข้อมูลต่างๆได้ (เหมือนประกาศตัวแปร global แต่ใช้ค่าผ่าน service และสามารถเก็บค่าได้ถาวรกว่า)

และ InMemoryWebApiModule ทำหน้าที่เป็น module ตัวกลางที่ใช้ในการสื่อสารระหว่าง database/client ผ่าน format เดียวกันกับที่ใช้ใน http

ในกรณีนี้ เราจึงสามารถใช้ InMemoryWebApiModule เพื่อปลอมแปลงเป็น Api แทนที่จะใช้ api จริงๆ

หลังจากมี api พร้อมในการ request กันแล้ว สิ่งหนึ่งที่จะไม่พูดถึงไม่ได้ คือรูปแบบของ api แน่นอนว่า api สำหรับแต่ละคนอาจจะมาไม่เหมือนกันแน่นอน แต่มีรูปแบบหนึ่งที่เป็นที่ยอมรับกันทั่วไปคือ REST architecture และในบทความนี้ จะอ้างอิง api รูปแบบนี้เป็นมาตรฐาน (ถ้าใช้รูปแบบอื่นก็ปรับเปลี่ยนตามแต่ที่ตัวเองถนัดได้เลยฮะ)

REST คือ แนวทางสำหรับกำหนดรูปแบบของ api โดยมองว่า api ที่ดีควรมี path และ method ที่ใช้ควรสื่อความหมายอย่างถูกต้อง, ทุกอย่างที่อยู่ใน api เป็นเหมือนก้อน resource ของระบบ และกำหนดการดำเนินการพื้นฐาน (Creat/Read/Update/Delete หรือ CRUD) ดังนี้

จะใช้ GET /hero สำหรับ ดึงรายการข้อมูล
จะใช้ GET /hero/10 สำหรับ ดึงข้อมูลชิ้นที่มี id=10
จะใช้ POST /hero สำหรับ สร้างข้อมูลใหม่
จะใช้ PUT /hero/10 สำหรับ แทนที่ข้อมูลชิ้นที่ id=10
จะใช้ DELETE /hero/10 สำหรับ ลบข้อมูลชิ้นที่ id=10

** อ่านรายละเอียดเพิ่มเติมได้ที่ Representational state transfer

Heroes and HTTP

ตอนนี้ก็ถึงเวลาที่เปลี่ยน HeroService ไปพึ่ง Api แล้ว เริ่มต้นด้วยการเปลี่ยน getHeroes()

เมื่อเราต้องใช้ Http เราก็ต้อง import มันเข้ามา

จากนั้นก็ inject มันเข้าไปใน HeroService แล้วตามด้วยการ GET /hero

** การทำงานคือ this.http.get(..) สร้าง http request เพื่อขอข้อมูลไปที่ api แบบ GET

ถึงแม้ว่าจะมีวิธีขอข้อมูลเป็นรูปแบบตายตัว แต่การรับข้อมูลจาก Http กลับสามารถทำได้หลายแบบ ไม่ว่าจะเป็น “Observable” ซึ่งเป็นการรับข้อมูลแบบ “reactive programming” ที่พูดถึงไปแล้วใน EP.6-2

หรือ เป็น Promise ซึ่งก็พูดถึงไปแล้วเช่นกันใน EP.5

แน่นอนว่า ในตอนนี้จะพูดถึงการรับข้อมูลทั้ง 2 รูปแบบ ก่อนอื่นเริ่มต้นด้วยการใช้ Promise โดยเปลี่ยน this.http.get(..) ซึ่งจะ คืนค่าเป็น Observable instance โดย default ไปเป็น Promise ผ่าน toPromise() ซึ่งเรา import มาในตอนต้น

จากนั้นก็เรียก .then(succeccCallback, failCallback) เพื่อรับค่าใส่ลงในตัวแปร

โดยเราสามารถจัดการกับ error request ได้ทันทีโดยใส่ที่ failCallback (อาร์กิวเม้นที่ 2) ของ .then(succeccCallback, failCallback) หรือใช้ .catch(failCallback) ก็ได้ผลลัพย์เช่นเดียวกัน

นอกจากนี้ เรายังสามารถแก้ getHero() ได้ด้วยวิธีการเดียวกัน

CRUD in HeroService

หลังจากมีเรียกใช้ GET /hero และ GET /hero/:id ไปแล้ว แน่นอนว่าต่อมาคงหนีไม่พ้น request เพื่อ update/create/delete hero และทั้งหมดก็ยังคงใช้รูปแบบเดียวกันกับ getHeroes() และ getHero()

จากโค้ด ดูเหมือนว่าทุกๆอย่างแทบจะเหมือนเดิม แค่เปลี่ยน endpoint url และ http method โดย

* create ใช้ POST
* update ใช้ PUT
* delete ใช้ DELETE

องค์ประกอบอีกอย่างที่เพิ่มขึ้นมา คือ this.headers

เพราะว่าในครั้งนี้มีข้อมูลที่ต้องส่งไปที่ api ดังนั้นจึงมีความจำเป็นที่ต้องแจ้งรูปแบบข้อมูลที่กำลังส่งไป ผ่าน “Content-Type” ที่อยู่ใน header

และข้อสังเกตที่สำคัญอีกจุด คือ http สามารถส่งข้อมูลได้เฉพาะตัวอักษร (string) เท่านั้น ดังนั้นจึงต้อง JSON.stringify() ก่อนการส่งข้อมูล

Updating hero details

กลับมาที่ view

หลายๆตอนที่ผ่านมา เราพยายามเขียน app ที่สามารถแก้ไข hero ได้แต่ดูเหมือนว่า หลังจากที่เปลี่ยนมาใช้ api แล้วแค่เปลี่ยนเพจไปมา ชื่อฮีโร่ที่เปลี่ยนแปลงไปก็กลับมาเป็นเหมือนเดิม

เพื่อบันทึกข้อมูล เราจะเริ่มต้นด้วยการสร้างปุ่ม save ลงใน HeroDetailComponent แล้วส่งข้อมูลล่าสุดไปให้ HeroService อัพเดตข้อมูล

Add the ability to add heroes

หลังจากแก้ไขได้แล้ว ต่อด้วยโค้ดเพื่อเพิ่มฮีโร่คนใหม่ โดยเริ่มจากการสร้าง input สำหรับกรอกชื่อฮีโร่ที่ต้องการพร้อมปุ่มตกลง ใน HeroesComponent จากนั้นก็รอรับข้อมูลเพื่อส่งต่อให้กับ HeroService

Add the ability to delete a hero

และสุดท้าย คือ ความสามารถในการลบ

แน่นอนว่า เรามีฟังก์ชั่นสำหรับลบภายใน HeroService พร้อมแล้วสิ่งที่เหลือ ก็แค่เพิ่มปุ่มเพื่อทำให้เกิด action โดยการเพิ่มปุ่ม delete ที่ HeroesComponent อีกครั้ง

ครั้งนี้แค่เพิ่ม (click) ไม่เพียงพอ เพราะว่า ปุ่มลบเป้าหมายของเราอยู่ภายใต้ <li> ซึ่งมี event click อยู่แล้ว จึงต้องเพิ่ม $event.stopPropagation() เพื่อระบุว่า ถ้าคลิกที่ตรงนี้แล้วไม่ต้องไปเรียก event ของ parent อีกต่อไป

ต่อด้วยการเขียน delete(hero)

แล้วสุดท้ายเติมแต่งด้วย css อีกเล็กน้อย

เพียงเท่านี้ ก็จะได้ “Tour of Heroes” อย่างสมบูรณ์ v1.0 เรียบร้อยแล้ว~

แต่บางคนคงคิดในใจว่า ไหนบอกว่าจะมีตัวอย่าง Observable ให้ดู?? แน่นอนว่า ไม่ลืมแน่นอน แต่ต้องขอยกไว้เป็นตอนต่อไป 7-2 //ทำตัวเหมือนแฮรี่ ตอนจบต้องมีภาคแยก

PS.

เนื้อหาทั้งหมด จะแบ่งเป็น 7 ตอน
EP1. introduction
EP2. The Hero Editor
EP3. Master/Detail
EP4. Multiple Components
EP5. Services
EP6. Routing
EP6. Routing II
EP7. HTTP
EP7. HTTP II (ver.Observable)
ตามดูโค้ดได้ที่ Github

Thank you

Angular.io บทความนี้อยู่ภายใต้ลิขสิทธิ์แบบ CC BY 4.0

Tell your friend about thisShare on Facebook
Facebook
0Tweet about this on Twitter
Twitter
Share on Google+
Google+
0