Note : บันทึก Workshop สร้าง Website ด้วย Node.js + Vue.js #Part 6

Artdvp
7 min readAug 20, 2018

--

Step 9 — Nuxt.js + API Proxy

การตั้งค่า Nuxt.js ให้เชื่อมต่อกับ API และการดึงข้อมูลด้วย Axios

โอเคมาเริ่มกันที่ Project Nuxt.js ที่ได้ทำไว้เมื่อพาร์ทก่อนๆ ผมจะทำการ Run มันขึ้นมาดูหน่อย โดยที่โปรเจค API เราก็ต้อง Run มันด้วยนะ

จากนี้จะทำการติดตั้งเครื่องมือที่ต้องใช้สำหรับติดต่อกับ API

ไปที่ Terminal แล้วใส่คำสั่งติดตั้ง package ในที่นี้จะใช้ Axios ในการเรียก Http Request

npm install vue-axios axios @nuxtjs/proxy

หลังจากติดตั้งเรียบร้อยแล้วให้ไปที่ folder plugins และทำการสร้างไฟล์ใหม่ชื่อ axios.js และใส่โค๊ดเพื่อกำหนด baseURL ให้เป็น /api สำหรับทุก request

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
const request = axios.create({
baseURL: '/api',
})
Vue.use(VueAxios, request)

จากนั้นไปที่ nuxt.config.js และกำหนดตั้งค่า

plugins: [
'~/plugins/vuetify',
'~/plugins/axios' //-- add plugins axios
],
proxy: {
'/api': 'http://127.0.0.1:8888', //-- ตั้งค่า map proxy url api server
ws: true
},
modules: [
'@nuxtjs/proxy' //-- add modules proxy
],
build: {
vendor: [
'vuetify',
'vue-axios' //-- add vendor vue-axios
],
}

มาดูที่การตั้งค่า Proxy API ที่ไฟล์ nuxt.config.js

proxy: {
'/api': 'http://127.0.0.1:8888', //-- ตั้งค่า map proxy url api server
ws: true
},

Proxy ในที่นี้คืออะไร ในโค๊ดจะทำการ Maping url ของ nuxt เข้ากับ url proxy ที่เราทำการตั้งค่าไว้เช่น

Nuxt
http://localhost:3000/#/api
=
express
http://127.0.0.1:8888/api
เมื่อทำการเรียกใช้ API
http://localhost:3000/#/api/student
ก็จะไปเรียก
http://127.0.0.1:8888/api/student

เนื่องจากหากยิง Request ไปที่ http://127.0.0.1:8888/api/student ตรงๆ Browser จะไม่ยอมให้ทำงานได้ เนื่องจากเกิด Cross-Origin Resource Sharing (CORS) หรือการเรียก Request ข้าม Domain บน Browser จึงต้องใช้งาน proxy มาทำการส่งต่อ request

ส่วนรูปแบบการยิง Request ของ Vue-axios ตามตัวอย่างด้านล่าง

this.$http.get(url, config)this.$http.post(url,data)this.$http({
url: '',
method: 'get'
})

หลังจากนั้นไปที่โฟลเดอร์ pages แล้วสร้างไฟล์ใหม่ชื่อ student.vue แล้วใส่โค๊ดตามภาพ

<script>
export default {
data() {
return {
students: []
}
},
async created() {
console.log('created')
let res = await this.$http.get('/student')
console.log(res.data)
}
}
</script>

Vuejs : Created

จากโค๊ด จะทำการประกาศ students เป็น Array และเพิ่มฟังก์ชัน created() ซึ่งก็คือฟังก์ชันเริ่มต้นของ vue.js ที่จะทำงานเมื่อ component ถูกสร้างเสร็จ สามารถดู Lifecycle ได้ที่ Document

การทำงานคือเมื่อเกิด created ให้ยิง Request ไปที่ path /student โดยประกาศตัวแปรมารับค่าชื่อ res แล้ว log ดูค่า จะใช้ async/await เมื่อเรียก api แล้วทำไมต้องใช้ async/await >> link

ทดสอบ npm run dev และไปที่ url : http://localhost:3000/#/student จะได้ ข้อมูล student ตามรายการในฐานข้อมูล

ถัดมาทำการแก้ไขโค๊ดที่ไฟล์ student.js เพื่อแสดงข้อมูล

หากสามารถดึงข้อมูลได้จะแสดงดังภาพ …

ตัวอย่างการส่ง Query string ด้วย Axios

async created() {
console.log('created')
// let res = await this.$http.get(`/student?class=${1}`)
let res = await this.$http.get('/student', {params: { class: 1 } })
console.log(res.data)
this.students = res.data.student
}

จากโค๊ดจะส่งค่า Query string : class=1 ไปที่ API ที่ทำไว้ ซึ่งตัว axios จะทำการ Generate ให้อยู่ในรูปแบบ http://localhost:3000/api/student?class=1

ค่าที่ได้ก็จะถูก Filter ด้วย class=1

ต่อมาจะทำการเพิ่ม select Dropdown สำหรับเลือก Filter ด้วยเลขชั้นปี (class) โดยแก้ไขไฟล์ /api/student.js

<select v-model="cls" @change="getStudent">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>

ทำการเพิ่ม Dropdown select โดย binding ค่าด้วย cls คือเลขชั้นปี(class) และหากมีการเลือกเปลี่ยนค่าให้เรียกใช้ @change=”getStudent”

export default {
data() {
return {
cls: '1',
students: []
}
},
async created() {
console.log('created')
//
this.getStudent()
},
methods: {
async getStudent() {
// let res = await this.$http.get(`/student?class=${this.cls}`)
let res = await this.$http.get('/student', { params: { class: this.cls}})
console.log(res.data)
this.students = res.data.student
}
}
}

ส่วนของ script จะทำการสร้าง methods > getStudent() และนำโค๊ดย้ายจาก created() มาไว้ใน getStudent()

ซึ่ง methods : getStudent() จะยิงไปที่ API เมื่อทำการเลือกค่าใน select dropdown และให้ทำงานครั้งที่ created() ด้วย

ไปดูที่หน้าเว็บ Dropdown จะถูก style ของ vuetify ทำให้มองไม่ออกว่าเป็น Dropdown แต่ให้ลองเลือกเปลี่ยนค่าดู จะเห็นว่ารายชื่อ student ก็จะเปลี่ยนไปตามที่เลือก Filter ด้วย class

ต่อมาจะทำการแก้ไขเพิ่ม Dropdown ของ vuetify และ Bining ข้อมูลสำหรับเลือกชั้นปีด้วย

จากตัวอย่างนี้จะพบปัญหาว่าเมื่อเราเลือกที่ Dropdown แต่ล่ะครั้งเมื่อเกิด onChange ถึงจะนำค่าไปยิง Request แต่ค่าที่ยิงจะไม่ใช่ค่าที่เลือกปัจจุบัน

ในภาพผมเลือกชั้นปีที่ 2 ก่อนและเมื่อผมเลือกชั้นปีที่ 3 มันจะเอาค่าชั้นปีที่ 2 ที่เลือกไว้ก่อนหน้าไปยิง Request ทำให้ข้อมูลไม่ถูกต้อง

Vuejs : Watcher

วิธีแก้ปัญหา : ให้ทำการยิง Request ไปที่ API เมื่อตัวแปร cls ถูกเปลี่ยน โดยใช้ watch ในการ Monitor ตัวแปรว่ามีการเปลี่ยนแปลงเมื่อไหร

ซึ่ง watch เป็นความสามารถของ vue.js

โดยจะไม่ใช้ @change="getStudent" ที่ <v-select/> ให้ลบออกไปซะ

แล้วใส่ watch และชื่อของตัวแปรที่เราต้องการ Monitor ว่ามีการเปลี่ยนแปลงหรือไม่นั่นคือ cls ซึ่งเมื่อค่าของ cls ถูกเปลี่ยนจะทำการเรียกใช้ this.getStudent() ไปยิง Request

กดเซฟและทำการ Refresh จอหนึ่งครั้งและลองเลือกที่ ชั้นปีที่ 3

จะเห็นว่าครั้งแรกที่เข้ามาจะยิง Request ไปที่ class=1 และเมื่อเลือก Dropdown ชั้นปีที่ 3 ก็จะยิง Request ไปที่ class=3 โอเคถูกต้อง

ในกรณีที่ใช้ watch แล้วต้องดูตัวแปร ค่าเก่าและค่าใหม่จะทำการใส่ parameter เพิ่มเข้าไปตามภาพ ซึ่งจะทดสอบให้แสดงผลค่าเก่ากับค่าใหม่ที่ console.log

กดเซฟและลองดูเลือกผลลัพธ์ โอเคใช้ได้

ถัดมาจะทำตัวอย่างการดึงข้อมูลมาทั้งหมด แต่จะใช้ filter ตามเงื่อนไขที่เลือก class

เริ่มต้นให้ทำการ copy ไฟล์ student.vue ให้คลิกที่ไฟล์ก่อนแล้วกด ctrl+c, ctrl+v แล้วเปลี่ยนชื่อเป็น student2.vue

จากนั้นทำการแก้ไขโค๊ดตามตัวอย่าง

เป็นการ get API ดึงข้อมูลนักเรียนทั้งหมด แล้วสร้างตัวแปรใน computed ชื่อ filteredStudent ทำการเช็คค่ากับตัวแปร cls และนำตัวแปร filteredStudent ไปใส่ที่ v-for แทน students

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

ถัดมาจะทำการสร้างโฟลเดอร์ชื่อ student เพื่อทำเป็นส่วนๆ และทำการสร้างไฟล์ใหม่ชื่อ edit.vue และ index.vue

เนื่องจากมีชื่อโฟลเดอร์และชื่อไฟล์ student เหมือนกัน การทำงานของ nuxt ก็คือไฟล์ที่ชื่อ student.vue จะเป็น page-parents ส่วนไฟล์ข้างในโฟลเดอร์ student จะเป็น page-child

ดังนั้นจึงต้องแก้ไขไฟล์ student.vue โดยให้ copy code ทั้งหมดใน file student.vue ไปวางที่ไฟล์ /student/index.vue

และทำการแก้ไขไฟล์ student.vue ตามภาพ โดยที่ page student.vue จะทำหน้าที่เป็น layout ของไฟล์ที่อยู่ในโฟลเดอร์ student ทุกไฟล์

ซึ่งเมื่อเปิดไปหน้า http://localhost:3000/#/student และ /edit

ก็จะมี Student Module ขึ้นเหมือนกันเพราะไฟล์ student.vue ทำหน้าที่คล้ายกับ layout แล้วนั่นเอง

จากนั้นจะทำการแก้ไขไฟล์ /student/edit.vue เพื่อทำหน้าแก้ไขข้อมูลโดยใส่โค๊ดตามภาพ

<template>
<div>
<h1>Student Edit</h1>
<v-text-field label="First Name"
v-model="fName"></v-text-field>
<v-text-field label="Laast Name"
v-model="lName"></v-text-field>
<v-btn>Save</v-btn>
<v-btn to="/student">Back</v-btn>
</div>
</template>

จากโค๊ดเราจะใช้ vuetify ในการทำ input ข้อมูลที่ชื่อว่า text-fields และใช้ v-btn ทำปุ่มบันทึกและปุ่ม back กลับไปที่หน้า student

แล้วทำการเพิ่มโค๊ดส่วน data และ methods สำหรับบันทึกข้อมูลไปที่ API

นั่นคือ เราจะทำการผูกค่า fname และ lname กับ input ด้วย v-model และสร้าง methods ชื่อ save เพื่อทำการ Post ข้อมูลไปที่ API เหมือนกับที่เราใช้ Postman ยิง

โดย id นั้นเราจะรับมาจาก Query String ซึ่งเวลาเรียกใช้คือ this.$route.query.id

เช่น http://localhost:3000/#/student/edit?id=3 โดยเมื่อลองเข้าดูก็จะพบกับ input เปล่าๆ เนื่องจากเราไม่ได้ดึงข้อมูลมาแสดง แต่ว่าเราสามารถแก้ไขค่าได้

ตัวอย่างผมจะทำการแก้ไข student ที่มี id=3

ลองใส่ค่าและกด save ถ้าทำงานถูกต้องจะขึ้น ok: true

จากนั้นลองมาดูที่ ฐานข้อมูล จะเห็นว่า fname และ lname ถูกเปลี่ยนแล้ว

หลังจากนั้นผมจะทำการเพิ่มโค๊ดที่ไฟล์ /student/edit.vue เพื่อให้เราเข้ามาที่หน้านี้ครั้งแรกให้มีข้อมูลเดิมแสดงไว้โดยจะเพิ่มในส่วน created()

async created(){
let res = await
this.$http.get(`/student/id/${this.$route.query.id}`)
console.log(res.data);
if(res.data.student){
this.fName = res.data.student.fname || ''
this.lName = res.data.student.lname || ''
}
},

โดย created() จะทำการยิง Request ไปที่ API ที่เรายังไม่ได้ทำไว้นั่นก็คือ /api/student/id/xx ซึ่ง xx ก็คือค่า id นั่นเอง

หน้าตา API เป็นแบบนี้ สำหรับ ดึงข้อมูล student จาก id

กดเซฟที่ไฟล์ แล้วลองเข้าไปที่ http://localhost:3000/#/student/edit?id=6 จะได้ข้อมูลตามภาพ ซึ่งก็จะสามารถแก้ไขข้อมูลได้เหมือนเดิม

ถัดมาจะไปที่ไฟล์ /student/index.js แล้วเพิ่มปุ่มสำหรับลิ้งไปหน้า edit โดยใช้ methods ในการเปลี่ยน router

กดเซฟแล้วไปที่หน้า http://localhost:3000/#/student/

กดที่ปุ่ม edit ชื่อ Pater Parker

ก็จะมาที่หน้าแก้ไข

จบ part 6 ครับ…

--

--