Skip to main content

เพิ่มหน้า Register

จากนี้เราจะเรียกหน้าที่เราทำมากันว่า Register Form คือ Form สำหรับการส่งข้อมูล user เข้าไปเก็บใน table: users

setup ก่อนส่ง Register form

เราจะทำการเชื่อม Register Form จากที่มีอยู่แล้ว โดยการไปที่ index.js ก่อน

  • ก่อนอื่น เพื่อให้ style code ทั้งหมดเหมือนกัน ผมจะขอปรับ style code ใน index.js ออกจากกันดังนี้
// เปลี่ยนจาก function submitData () { ... }
const submitData = () => {
...
}

หลังจากนั้นสร้าง function ใหม่ขึ้นมาสำหรับส่งข้อมูล โดยรอบนี้เราจะปรับจาก Promise เป็น async, await เพื่อให้เหมือนกันทั้ง javascript ฝั่งหน้าบ้านและ nodejs (javascript) ฝั่งหลังบ้าน

เพิ่ม async ใน submitData
const submitData = async () => {
...
let userData = {
firstname: firstNameDOM.value,
lastname: lastNameDOM.value,
age: ageDOM.value,
gender: genderDOM.value,
description: descriptionDOM.value,
interests: interest
}

// เราจะเริิ่ม code จากตรงนี้กัน
}

ทำตัวส่ง Register ผ่าน API

const submitData = async () => {
...
let userData = {
firstname: firstNameDOM.value,
lastname: lastNameDOM.value,
age: ageDOM.value,
gender: genderDOM.value,
description: descriptionDOM.value,
interests: interest
}

// submit data
try {
const response = await axios.post(
'http://localhost:8000/users',
userData
)
console.log('response data', response.data)
} catch (error) {
console.error(error)
}
}

จะพบว่าข้อมูลส่งเข้าแล้วเรียบร้อย เรามาลองทดสอบส่งข้อมูลผ่าน Form กัน register-1

เพื่อให้เกิดความสวยงาม เราจะนำข้อมูลการส่งเรียบร้อยออกมาแสดงผ่านหน้าจอ

  1. เพิ่มจุดแสดงข้อความใน html
...
<!-- เพิ่มบน ปุ่มส่งข้อมูล ขึ้นมา -->
<div id="response-message" class="message"></div>
<div class="center">
<button onclick="submitData()" class="button">ส่งข้อมูล</button>
</div>
...
  1. เพิ่ม style สำหรับการแสดง message
.message {
padding: 10px;
background-color: lightgray;
margin: 10px 0;
display: none;
}

.message.success {
background-color: lightgreen;
display: block;
}

.message.danger {
background-color: lightcoral;
display: block;
}
  1. เพิ่ม javascript สำหรับการ select DOM ไปยังจุดแสดงข้อความ (id = response-message) และแสดงข้อความออกมา
const submitData = async () => {
...
// เพิ่ม
let responseMessageDOM = document.getElementById('response-message')
// ย้าย try มาอยู่ก่อนส่งข้อมูลทั้งหมด
try {
let interest = ''

for (let i = 0; i < interestDOMs.length; i++) {
interest += interestDOMs[i].value
if (i != interestDOMs.length - 1) {
interest += ', '
}
}

let userData = {
firstname: firstNameDOM.value,
lastname: lastNameDOM.value,
age: ageDOM.value,
gender: genderDOM.value,
description: descriptionDOM.value,
interests: interest
}

const response = await axios.post(
'http://localhost:8000/users',
userData
)
// หลัง response จาก axios เรียบร้อย = แสดง error message ออกมา
responseMessageDOM.innerText = 'เพิ่มข้อมูลเรียบร้อย !'
responseMessageDOM.className = 'message success'
} catch (error) {
// เกิด error อะไรก็ตาม ให้แสดงว่ามีปัญหาเกิดขึ้นออกมา
responseMessageDOM.innerText = 'มีปัญหาเกิดขึ้น'
responseMessageDOM.className = 'message danger'
}

การ handle Error

ปกติการ handle Error จะสามารถทำได้จากทั้ง 2 ฝั่งคือฝั่ง Frontend (Validation จาก Form หน้าบ้าน) และฝั่ง Backend (Validate จาก field ที่ส่งมาผ่าน API) เราจะทำให้ดูทั้ง 2 วิธี

1. handle Error จากฝั่งหน้าบ้าน

  1. เพิ่มการรองรับ DOM (เพิ่ม || ) ให้กับพวกที่มี :checked เพื่อไม่ให้เกิด key error ตอนเลือก field (ไม่งั้นจุดนี้จะมีโอกาสเป็น null ได้)
const submitData = async () => {
let firstNameDOM = document.querySelector('input[name=firstname]')
let lastNameDOM = document.querySelector('input[name=lastname]')
let ageDOM = document.querySelector('input[name=age]')
let genderDOM = document.querySelector('input[name=gender]:checked') || {}
let interestDOMs = document.querySelectorAll('input[name=interest]:checked') || {}
let descriptionDOM = document.querySelector('textarea[name=description]')
...
}
  1. เพิ่ม function สำหรับการ validate ขึ้นมา
const validateData = (userData) => {
let errors = []
if (!userData.firstname) {
errors.push('กรุณาใส่ชื่อจริง')
}
if (!userData.lastname) {
errors.push('กรุณาใส่นามสกุล')
}
if (!userData.age) {
errors.push('กรุณาใส่อายุ')
}
if (!userData.description) {
errors.push('กรุณาใส่คำอธิบาย')
}
if (!userData.interests) {
errors.push('กรุณาเลือกความสนใจอย่างน้อย 1 อย่าง')
}
return errors
}

const submitData = async () => {
...

try {
let userData = {
firstname: firstNameDOM.value,
lastname: lastNameDOM.value,
age: ageDOM.value,
gender: genderDOM.value,
description: descriptionDOM.value,
interests: interest
}

// เพิ่มการเรียกใช้ validate ขึ้นมา
const errors = validateData(userData)

// ถ้าเจอ error อย่างน้อย 1 ตัว
if (errors.length > 0) {
throw {
message: 'กรอกข้อมูลไม่ครบ',
errors: errors
}
}
} catch (error) {
let messageDOM = 'มีปัญหาเกิดขึ้น'
if (error.errors && error.errors.length > 0) {
messageDOM = '<div>'
messageDOM += `<div>${error.message}</div>`
messageDOM += '<ul>'
for (let i = 0; i < error.errors.length; i++) {
messageDOM += `<li>${error.errors[i]}</li>`
}
messageDOM += '</ul>'
messageDOM += '</div>'
}
responseMessageDOM.innerHTML = messageDOM
responseMessageDOM.className = 'message danger'
}
}

และนี่ก็คือผลลัพธ์ของเรื่องราวนี้ register-2

2. handle Error จากฝั่งหลังบ้าน

  1. เพิ่ม function validate validateData() แบบเดียวกันใน server/index.js
const validateData = (userData) => {
let errors = []
if (!userData.firstname) {
errors.push('กรุณาใส่ชื่อจริง')
}
if (!userData.lastname) {
errors.push('กรุณาใส่นามสกุล')
}
if (!userData.age) {
errors.push('กรุณาใส่อายุ')
}
if (!userData.description) {
errors.push('กรุณาใส่คำอธิบาย')
}
if (!userData.interests) {
errors.push('กรุณาเลือกความสนใจอย่างน้อย 1 อย่าง')
}
return errors
}
  1. เรียกใช้ validate ใน function posts
const validateData = (userData) => { ... }

app.post('/users', async (req, res) => {
try {
let user = req.body

// เพิ่ม code สำหรับ validate
const errors = validateData(user)

if (errors.length > 0) {
throw {
errorMessage: 'กรอกข้อมูลไม่ครบ',
errors: errors
}
}

const results = await conn.query('INSERT INTO users SET ?', user)
res.json({
message: 'insert ok',
data: results[0]
})
} catch (error) {
const message = error.errorMessage || 'something wrong' // เพิ่ม handle message
res.status(500).json({
message: message,
errors: error.errors || [] // ส่ง array error เข้าไปผ่าน body
})
}
})
  1. เพิ่มการ handle error ฝั่ง Frontend ให้ support ฝั่ง Backend ด้วย

const submitData = async () => {
try {
...

// ลอง comment Frontend เพื่อทดสอบ
// const errors = validateData(userData)

// if (errors.length > 0) {
// throw {
// message: 'กรอกข้อมูลไม่ครบ',
// errors: errors
// }
// }

} catch (error) {
let messageDOM = 'มีปัญหาเกิดขึ้น'
// ปรับตัวแปรให้รองรับใช้ได้ทั้ง 2 ฝั่ง โดย error.errors คือฝั่งของ Frontend
let errors = error.errors || []
let errorMessage = error.message

// เพิ่ม code สำหรับรับ error message ของ Backend
if (error.response && error.response.data) {
errors = error.response.data.errors
errorMessage = error.response.data.message
}

if (errors && errors.length > 0) {
messageDOM = '<div>'
messageDOM += `<div>${errorMessage}</div>`
messageDOM += '<ul>'
for (let i = 0; i < errors.length; i++) {
messageDOM += `<li>${errors[i]}</li>`
}
messageDOM += '</ul>'
messageDOM += '</div>'
}
responseMessageDOM.innerHTML = messageDOM
responseMessageDOM.className = 'message danger'
}
}

คำถามก็คือ เราควร handle จากฝั่ง Frontend หรือ Backend ดี ?

  • จริงๆควรทำทั้ง 2 ฝั่ง แต่สามารถตกลงกันได้ว่า ฝั่งไหนจะควบคุม Error message ที่ user เห็น, ฝั่งไหนควบคุม error code
  • ส่วนใหญ่จะให้ ฝั่ง Backend ส่ง error code มา แต่ให้ Frontend ควบคุมข้อความออกมา