keyboard_tabSkip to content
listWeb Development 101

เพิ่มหน้า Register

สารบัญ

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

setup ก่อนส่ง Register form

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

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

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

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

javascript
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
html
...
    <!-- เพิ่มบน ปุ่มส่งข้อมูล ขึ้นมา -->
    <div id="response-message" class="message"></div>
    <div class="center">
      <button onclick="submitData()" class="button">ส่งข้อมูล</button>
    </div>
...
  1. เพิ่ม style สำหรับการแสดง message
css
.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) และแสดงข้อความออกมา
javascript
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 ได้)
javascript
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 ขึ้นมา
javascript
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
javascript
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
javascript
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 ด้วย
javascript

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 ควบคุมข้อความออกมา