ต่อจากบทความที่แล้ว ที่ค้างไว้นาน ทำ Application Offline ด้วย VueJs
เรามาต่อกันด้วย อีก 1 กุญแจสำคัญในการทำ Web Application Offline ของเราคือตัว IndexedDB กันครับ ซึ่งจุดประสงค์ในการใช้มันก็คือ เราต้องเก็บข้อมูลไว้บน Browser ของ Client โดยมันจะเก็บอยู่ในรูปแบบของ Object ที่เราสามารถ CRUD มันได้ โดยที่ไม่ต้องเชื่อมต่อ Internet ก็ได้ทำให้ Web app เราสามารถ จัดเก็บข้อมูลไว้ได้ตลอดเวลาครับ
Using IndexedDB
IndexedDB is a way for you to persistently store data inside a user’s browser. Because it lets you create web applications with rich query abilities regardless of network availability, your applications can work both online and offline.
เรามาเริ่มลองทำกันไปที่ละขั้นตอนกันเถอะ ก่อนอื่นเราต้องติดตั้ง สิ่งที่จะช่วยให้เราทำงานง่ายขึ้นก็คือ
- pinia ตัวนี้จะมาช่วยทำให้ share state ระหว่าง component ได้
- vue router เอามาช่วยทำ pwa เพื่อให้เราสามารถ link แต่ละหน้าได้ (ในบทความนี้อาจจะยังไม่ได้มาก แต่มันจำเป็นแน่ๆ สำหรับ ทำ pwa ครับ)
- สร้างไฟล์
todo.js
ใน foldersrc/store
เพื่อทำการเชื่อมต่อ IndexedDB กันก่อนครับ โดยเราจะใช้ตัว pinia มาช่วยตรงนี้ด้วยครับ
ในไฟล์นี้จะประกอบไปด้วย state ที่เราจะเอาไว้ระบุ ชื่อของ Database name และ Database version และ ระบุ Connection Database เอาไว้
มาถึง function initDatabase() กันต่อ ตรงนี้จะเป็น function ที่เอาไว้ connect กับ IndexedDB และ ทำการสร้าง table ที่เราต้องการ โดยจะสร้าง table ตรง บรรทัดที่ 36–37 ใน table todos
ของเรานั้นจะต้องระบุ keyPath ด้วย (คล้ายๆ primary key) ในที่นี้จะใช้ว่า id ไปเลยครับ
ในบรรทัดที่ 31–34 นั้นจะเอาไว้สำหรับ ลบ table ถ้าหากต้องต้องแก้ไข table เราจะต้องทำการ แก้ไข Database version ก่อน เสร็จแล้ว ต้องสั่งมันลบ table เดิม แล้วค่อยไปสร้างใหม่ครับ
2. จากนั้นเราไปแก้ไขไฟล์ App.vue เปลี่ยน component จาก HelloWorld ไปเป็น component ใหม่ ในที่นี้ผมจะใช้ชื่อว่า Todo.vue ครับ โดยใน component นั้นผมจะทำการ import store/todo เข้ามา จากนั้น ทำการประกาศ todoStore
ด้วย useTodoStore()
แล้วใน onMounted เราก็จะทำการเรียก function initDatabase กันครับ
แล้วเมื่อเราลองเข้าหน้า web ดูอีกที แล้วเปิด tab inspect ขึ้นมา ไปที่ tab Application ซ้ายมือตรง Storage -> IndexedDB เราจะเห็นชื่อ Database ที่เราสร้างขึ้นมา โดยในนั้นจะมี table ที่เราสร้างขึ้นมาด้วยคือ todos ครับ
3. ต่อไปเรามาลองสร้าง function เพื่อ insert ข้อมูลเข้า table ของเรากันครับ
ในไฟล์ todo.js
เราจะทำการเพิ่ม 1 function สำหรับ insert Todo ครับ โดยใน function นั้นเราจะมีการ อ้างถึง ตัวแปร database ไว้ เพื่อเป็นการระบุว่า ให้เชื่อมต่อ connection อันไหน และ อ้างถึง table ที่เราต้องการ จากนั้น ถ้าหากเชื่อมต่อสำเร็จ ก็จะทำการ add() ตัวแปร data ที่เราส่งเข้ามา เข้า table ไปครับ
ส่วนตัวไฟล์ Todo.vue เราก็จะทำเพิ่ม code ตามภาพนี้ หลักๆคือ ทำ function submit มาเพื่อเพิ่มข้อมูล โดยจะทำการส่งค่าที่เราต้องการเข้าไปในตัว todoStore ครับผ่าน function add() นั้น เราจะต้องทำการสร้างตัวแปร มาเพื่อส่งข้อมูลเข้าไป insert ตามรูปแบบของ table เรา คือต้องมี id ในที่นี้ผมจะใช้ dateTime แทนหรือเพื่อนๆจะใช้รูปแบบ increment id เองก็ได้ครับ หลังจากที่เรา กด Add ที่ form แล้วมันจะทำการ Insert ข้อมูลให้เราไปที่ table todos ให้ครับ
* ถ้าหาก Data ไม่แสดงให้ กดเครื่องหมาย reload (ข้างใต้คำว่า memory ตามภาพครับ เพื่อ)
** ผมใช้ pure css grid มาใช้ในการทำ grid ครับ
4. ที่นี้เรามาลอง ดึงข้อมูลใน table มาแสดงผลกันครับ
เพิ่ม function getTodos() เพิ่มเข้ามา โดยจะระบุ table ที่ต้องการแล้ว ทำการ getAll() มาครับ
จากนั้นในไฟล์ Todo.vue เราก็จะเพิ่ม function getData() ขึ้นมาเพื่อ ดึง data จาก indexedDB มาแสดง แล้ว ใส่เข้าไปในตัวแปร todos ที่ประกาศด้วย ref ที่ default เป็น Array ไว้ครับ ทุกครั้งที่โหลดหน้า หรือ เพิ่มข้อมูลสำเร็จ เราจะเรียก getData() เพื่อ แสดงข้อมูลล่าสุดครับ
ทุกครั้งที่เรา เพิ่ม todo เข้าไป My todo list ทางด้านซ้ายจะก็จะอัปเดตให้เห็นข้อมูลล่าสุดเสมอครับ ถึงแม้ว่าเราจะปิด internet ไป ข้อมูลก็ยังจะแสดงอยู่ เพราะนี้คือความสามารถของตัว indexedDB ที่จัดเก็บข้อมูลไว้บนเครื่อง Client ครับ
5. ที่เหลือจะเป็น Update กับ Delete นะครับ เราจะไปกันเร็วๆ
สำหรับ update นั้น ในไฟล์ todo.js
เราจะทำการเพิ่ม 1 function สำหรับ getTodo ขึ้นมาครับ โดยค่าที่ต้องส่งเข้าไปจะต้องเป็น id (หรือ primary key ที่เราได้ระบุไว้นะครับ)
ส่วนในไฟล์ Todo.vue เราก็จะทำการแก้ไขเพิ่มนิดหน่อย โดยการใส่ ปุ่มเพื่อเรียกใช้ function getTodo เรา และ เมื่อได้ค่ามา เราจะ assign กลับเข้าไปที่ textarea นั้นเพื่อให้สามารถ อัปเดตข้อมูลได้ โดยผมได้ใช้วิธีการ เพิ่มตัวแปร myTodoUpdateId ไว้เป็น ref เพื่ออ้างถึง id ที่เราจะแก้ไขครับ (อันนี้ผมทำง่ายๆนะครับ เพื่อให้เห็นภาพตรงกันเฉยๆ เอาไปใช้จริงไม่ได้)
จากนั้นเราก็มีแก้ไข function submit ด้วย ถ้าหาก เราจะทำการ update รายการก็จะให้ใช้ค่า id จากตัวแปรใหม่นั้นแทน คราวนี้ เราก็ต้องไปแก้ไข function CreateTodo ใหม่ ให้มันสามารถ Update ได้ด้วย
ใน function นี้ เราจะเพิ่มส่วนที่ค้นหา รายการจาก id ก่อน ถ้าเจอ เราจะ อัปเดตค่าเข้าไป แล้ว put เพื่อ update มัน ส่วนถ้าไม่เจอ ก็จะเป็นการ create เหมือนเดิมครับ
6. ส่วนของการ Delete นั้นจะคล้ายๆกับ getTodo ครับ ผมจะเพิ่ม function removeTodo ไว้ใน Todo.vue ครับ โดยจะส่ง id เข้าไป แล้วให้มันไปเรียก function deleteTodo() ครับ
เพียงเท่านี้ เราก็จะสามารถ Insert Update Delete และ Retriev Data ออกมาจาก IndexedDB ได้แล้วครับ
เดียวครั้งต่อไปเราจะมีสรุปทั้งหมด และ อธิบายถึงการทำ App ของเราให้สามารถใช้งานได้ในขณะที่ Offline และเมื่อกลับมา Online แล้ว มันจะ Sync ข้อมูลกลับขึ้น Server ยังไงบ้างครับ
สำหรับ SourceCode ตัวอย่าง Project นี้ สามารถ Clone ได้จากนี้เลยครับ PWA101
Bonus Track: ทั้งหมดนี้ สามารถใช้ตัว Firebase RealTime Database และ ตัว Cloud Firestore ได้ด้วยเหมือนกันนะครับ สะดวกกว่าด้วย เพื่อเป็นทางเลือกอีกทางให้ศึกษาครับ
Credit by Natdanai Upathong