อย่างที่ได้เกริ่นไปแล้วในเนื้อหาในตอนที่ผ่านมาว่า เราจะ
ทำระบบเกี่ยวกับสมาชิก ซึ่งระบบสมาชิกนั้น จะมีองค์ประกอบมากมาย
ที่จะเข้ามาเกี่ยวข้อง บางส่วนเราอาจจะได้รู้จัก และทำความเข้าใจเบื้องต้น
ไปบ้างแล้ว เช่น
การใช้งาน EJS Template Engine http://niik.in/911
การตรวจสอบความถูกต้องของข้อมูลด้วย Joi http://niik.in/914
การเชื่อมต่อกับฐานข้อมูล MongoDB http://niik.in/917
สำหรับบางส่วนที่เรายังไม่ได้ทำความรู้จัก เช่น
การกำหนด Template Layout
การใช้งาน Cookies , การใช้งาน Session
การเข้ารหัสข้อมูลเช่น รหัสผ่าน
และอาจจะมีรายละเอียดปลีกย่อยเพิ่มเติม ดังนั้น เนื้อหาเกี่ยวกับระบบสมาชิก ก็จะเป็นเนื้อหา
แยกย่อยไป ตามขั้นตอน พร้อมทำความรู้จัก และใช้งานส่วนที่เกี่ยวข้องต่างๆ
ในการศึกษาบทความ หากไม่สามารถทำตามในทุกขั้นตอน แนะนำให้ทำความเข้าใจ ถึงที่มาที่ไป
ของโค้ดในแต่ละส่วน เพื่อไล่ลำดับความเข้าใจ โดยจะพยายามไม่ใช่ Module พิเศษ เช่น บาง
Module มีรูปแบบการใช้งานง่าย กำหนดค่าไม่กี่ขั้นตอน ก็สามารถใช้งานได้ แต่ก็ทำให้เราไม่ทราบถึงกระบวน
การทำงาน ดังนั้น อาจจะได้เจอ Module พื้นฐาน เป็นแนวทางสำหรับใช้งาน Module พิเศษอื่นๆ ต่อไป
เนื้อหาเกี่ยวกับระบบสมาชิก ตอนแรกนี้ เราจะมาดูถึงการสร้างหน้าเว็บเพจ โดยใช้งาน EJS Template ให้กับ
ระบบสมาชิก โดยจะใช้การจัดการด้วย Template Layout
ตัวอย่าง Template Layout
<?- include('header') -?> <h1> Title </h1> <p> My page </p> <?- include('footer') -?>
โครงสร้างไฟล์ Template ของระบบสมาชิก
ในระบบสมาชิกของเราจะประกอบไปด้วย หน้าล็อกอิน หน้าสมัครสมาชิก และหน้าสมาชิก
- login.ejs
- register.ejs
- dashboard.ejs
โดยทั้งสาม ไฟล์ เราสร้างไว้ในโฟลเดอร์ "views/pages" และรวมสองไฟล์ หน้าแรก และหน้า error
- index.ejs หน้า Home Page
- error.ejs หน้า Error Page
และในแต่ละหน้าเพจ อาจจะมีบางส่วนที่มีการใช้งานเหมือนกัน เช่นมีส่วนของ header footer เหล่านี้
เป็นต้น เราจะทำการสร้างแยกเป็นส่วนไว้ในโฟลเดอร์ "views/partials" ได้แก่ไฟล์
- footer.ejs
- head.ejs
- header.ejs
- nav.ejs
และนี่คือทั้งหมดของไฟล์ template ของโปรเจ็คระบบสมาชิก เริ่มต้นของเรา
เรามาดูโค้ดส่วนของ partials กันก่อน ตามลำดับดังนี้ [views/partials]
ไฟล์ head.ejs [views/partials/head.ejs]
<head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title><?= pageData.title ?></title> <link rel="stylesheet" href="https://unpkg.com/bootstrap@4.3.1/dist/css/bootstrap.min.css" > <link rel="stylesheet" href="css/mycss.css"> <script src="https://unpkg.com/jquery@3.3.1/dist/jquery.min.js"></script> <script src="https://unpkg.com/bootstrap@4.3.1/dist/js/bootstrap.min.js"></script> </head>
ไฟล์ header.ejs [views/partials/header.ejs]
<div class="container"> <header class="bg-light text-center"> THIS IS HEADER </header> </div>
ไฟล์ nav.ejs [views/partials/nav.ejs]
<nav class="text-center"> THIS IS NAV <br> <a href="/login">Login</a> | <a href="/register">Register</a> | <a href="/me/logout">Logout</a> </nav>
ไฟล์ footer.ejs [views/partials/footer.ejs]
<footer class="bg-light text-center"> THIS IS FOOTER </footer>
โปรเจ็คของเราจะมีการใช้งาน Bootstrap CSS Framework จะเห็นได้จากการกำหนดการใช้งาน css class
ของ Bootstrap นอกจากนั้นเรายังเรียกใช้งานไฟล์ css ที่กำหนดเองในโฟลเดอร์ public ที่กำหนด static path
ไว้ ให้สามารถเรียกใช้งานผ่าน 'css/mycss.css' จากบทความ http://niik.in/909
ต่อไปมาดูในส่วนของไฟล์ template ของแต่ละเพจ โดยจะเริ่มในส่วนที่เกี่ยวข้องโดยตรงกับระบบสมาชิกก่อน
ไฟล์เหล่านี้จะอยู่ในโฟลเดอร์ [views/pages] เราจะเริ่มเห็นการกำหนด Template Layout ในส่วนเหล่านี้
ไฟล์ login.ejs [views/pages/login.ejs]
<!doctype html> <html> <?- include('../partials/head') -?> <body> <?- include('../partials/header') -?> <?- include('../partials/nav') -?> <div class="container"> <h1 class="text-center">LOGIN</h1> <form class="mt-3" action="/login" method="POST" novalidate> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="email" class="form-control" name="email" placeholder="Your email"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="password" class="form-control" name="password" placeholder="Your password"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <button type="submit" class="btn btn-primary btn-block mx-auto"> Login</button> </div> </div> </form> </div> <?- include('../partials/footer') -?> </body> </html>
ไฟล์ register.ejs [views/pages/register.ejs]
<!doctype html> <html> <?- include('../partials/head') -?> <body> <?- include('../partials/header') -?> <?- include('../partials/nav') -?> <div class="container"> <h1 class="text-center">REGISTER</h1> <form class="mt-3" action="/register" method="POST" novalidate> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="text" class="form-control" name="name" placeholder="Your name"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="email" class="form-control" name="email" placeholder="Your email"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="password" class="form-control" name="password" placeholder="Your password"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="password" class="form-control" name="confirm_password" placeholder="Confirm your password"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <button type="submit" class="btn btn-primary btn-block mx-auto"> Register</button> </div> </div> </form> </div> <?- include('../partials/footer') -?> </body> </html>
ไฟล์ dashboard.ejs [views/pages/dashboard.ejs]
<!doctype html> <html> <?- include('../partials/head') -?> <body> <?- include('../partials/header') -?> <?- include('../partials/nav') -?> <div class="container"> <h1 class="text-center">Hello</h1> <div class="text-center my-3 border border-success"> John Doe </div> <div class="text-center"> </div> </div> <?- include('../partials/footer') -?> </body> </html>
หน้าเพจอื่นๆ เพิ่มเติม
ไฟล์ index.ejs [views/pages/index.ejs]
<!doctype html> <html> <?- include('../partials/head') -?> <body> <?- include('../partials/header') -?> <?- include('../partials/nav') -?> <div class="container text-center"> <h1><?= pageData.title ?></h1> <p>Welcome to <?= pageData.title ?></p> </div> <?- include('../partials/footer') -?> </body> </html>
ไฟล์ error.ejs [views/pages/error.ejs]
<!doctype html> <html> <?- include('../partials/head') -?> <body> <?- // include('../partials/header') -?> <?- // include('../partials/nav') -?> <div class="container"> <h1><?= message ?></h1> <h2><?= error.status ?></h2> <pre><?= error.stack ?></pre> </div> <?- // include('../partials/footer') -?> </body> </html>
สำหรับหน้า error.ejs จะใช้งานร่วมกับการจัดการ Error Handling จากบทความ http://niik.in/912
สำหรับเนื้อหา และการใช้งาน EJS เบื้องต้น ดูได้ที่บทความ http://niik.in/911
การเชื่อมต่อฐานข้อมูล MongoDB
ต่อด้วยส่วนสำหรับเชื่อมต่อก้บฐานข้อมูลไฟล์ db.js ในโฟลเดอร์ [config/db.js]
ไฟล์ db.js [config/db.js]
const MongoClient = require('mongodb').MongoClient // ใช้งาน mongodb module const url = 'mongodb://localhost:27017' // กำหนด url สำหรับ MongoDB Server const dbName = 'testdb' // กำหนดชื่อฐานข้อมูลที่จะใช้งาน // ส่งการเชื่อมต่อฐานข้อมูลไปใช้งาน module.exports = new Promise((resolve, reject)=>{ MongoClient.connect(url, { useNewUrlParser: true }, (error, client) => { if (error) throw error var db = client.db(dbName) console.log("Connected successfully to server") resolve(db) }) })
การกำหนด Routes Path
ต่อไปมาส่วนของการกำหนด router ซึ่งประกอบไปด้วยการจัดการ routes path หน้า index, login, register
และ dashboard
ไฟล์ index.js [routes/index.js]
ส่วนของหน้าแรก เรียกผ่าน path: "/" ซึ่งเราจะกำหนดในไฟล์ app.js
const express = require('express') const router = express.Router() router.get('/', function(req, res, next) { res.locals.pageData = { title:'Home Page' } res.render('pages/index') }) module.exports = router
ไฟล์ login.js [routes/login.js]
ส่วนของหน้าล็อกอิน เรียกผ่าน path: "/login" ซึ่งเราจะกำหนดในไฟล์ app.js
const express = require('express') const router = express.Router() router.route('/') .all((req, res, next) => { res.locals.pageData = { title:'Login Page' } next() }) .get((req, res, next) => { res.render('pages/login') }) .post((req, res, next) => { res.redirect('/me') }) module.exports = router
ไฟล์ register.js [routes/register.js]
ส่วนของหน้าสมัครสมาชิก เรียกผ่าน path: "/register" ซึ่งเราจะกำหนดในไฟล์ app.js
const express = require('express') const router = express.Router() router.route('/') .all((req, res, next) => { res.locals.pageData = { title:'Register Page' } next() }) .get((req, res, next) => { res.render('pages/register') }) .post((req, res, next) => { res.render('pages/register') }) module.exports = router
ไฟล์ dashboard.js [routes/dashboard.js]
ส่วนของหน้าสมาชิก เรียกผ่าน path: "/me" และ "/me/logout" ซึ่งเราจะกำหนดในไฟล์ app.js
const express = require('express') const router = express.Router() router.route('/') .get((req, res, next) => { res.locals.pageData = { title:'Dashboard Page' } res.render('pages/dashboard') }) router.route('/logout') .get((req, res, next) => { res.redirect('/login') }) module.exports = router
สังเกตว่าแต่ละ routes เรามีการส่งค่าข้อมูลเกี่ยวกับหน้าเพจนั้น โดยใช้ res.locals เป็น property ของ
Response ค่าที่กำหนดด้วย res.locals จะสามารถใช้งานได้เฉพาะภายใน views ที่ทำการ render เข้าใจง่ายๆ
ก็คือกำหนดค่า สำหรับส่งเข้าไปในหน้าเพจ ที่กำลังจะแสดงด้วยคำสั่ง res.render()
อย่างในโค้ดข้างต้น เราต้องการส่งค่าตัวแปร pageData.title ไปแสดงแสดงใหนส่วนของ <title></title> เพจ
จากนั้นนำส่วนต่างมากำหนดในไฟล์ app.js จะได้เป็นดังนี้
ไฟล์ app.js
const express = require('express') // ใช้งาน module express const app = express() // สร้างตัวแปร app เป็น instance ของ express const path = require('path') // เรียกใช้งาน path module const createError = require('http-errors') // เรียกใช้งาน http-errors module const port = 3000 // port // ส่วนของการใช้งาน router module ต่างๆ const indexRouter = require('./routes/index') const loginRouter = require('./routes/login') const registerRouter = require('./routes/register') const dashboardRouter = require('./routes/dashboard') // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); app.set('view options', {delimiter: '?'}); // app.set('env','production') app.use(express.json()) app.use(express.urlencoded({ extended: false })) app.use(express.static(path.join(__dirname, 'public'))) // เรียกใช้งาน indexRouter app.use('/', indexRouter) app.use('/login', loginRouter) app.use('/register', registerRouter) app.use('/me', dashboardRouter) // ทำงานทุก request ที่เข้ามา app.use(function(req, res, next) { var err = createError(404) next(err) }) // ส่วนจัดการ error app.use(function (err, req, res, next) { // กำหนด response local variables res.locals.pageData = { title:'Error Page' } res.locals.message = err.message res.locals.error = req.app.get('env') === 'development' ? err : {} // กำหนด status และ render หน้า error page res.status(err.status || 500) // ถ้ามี status หรือถ้าไม่มีใช้เป็น 500 res.render('pages/error') }) app.listen(port, function() { console.log(`Example app listening on port ${port}!`) })
เราสามารถทำสอบรัน app.js แล้วดูผลลัพธ์การทำงานเบื้องต้นได้ ซึ่งหน้าตาก็จะแสดงเหมือนรูปที่ประกอบหน้า
template ของแต่ละไฟล์ด้านบนที่ผ่านมา
Node Module เพิ่มเติม
Module ต่างๆ ที่เราทำการ install หรือติดตั้งในตอนเริ่มนี้ จะได้แก่
"dependencies": { "@hapi/joi": "^15.0.0", "ejs": "^2.6.1", "express": "^4.16.4", "mongodb": "^3.2.3" }
การตรวจสอบความถูกต้องของข้อมูล
ต่อไปเป็นส่วนของการตรวจสอบความถูกต้องขอข้อมูล ที่ส่งมาจากฟอร์ม ซึ่งเราใช้งาน Joi Module เราจะกำหนด
ฟังก์ชั่นตรวจสอบความถูกต้องของข้อมูล รวมถึงกำหนด schema หรือรูปแบบของข้อมูลในไฟล์ users.js
ไว้ในโฟลเดอร์ [validator/users.js]
ไฟล์ users.js [validator/users.js]
const Joi = require('@hapi/joi') const validation = (schema) =>{ return ((req, res, next) => { // ทำการตรวจสอบความถูกต้องของข้อมูล req.body ที่ส่งมา Joi.validate(req.body, schema, function (error, value) { // กรณีเกิด error ข้อมูลไม่ผ่านการตรวจสอบ if(error) { res.locals.errors = { "message": error.details[0].message } res.locals.user = req.body return res.render(req.renderPage) } if(!error) next() }) }) } // กำหนดชุดรูปแบบ schema const schema = { register : Joi.object().keys({ name: Joi.string().min(3).max(30).required(), email: Joi.string().email({ minDomainSegments: 2 }).required(), password:Joi.string().min(6).max(15).required(), confirm_password: Joi.any().valid(Joi.ref('password')).required() .error(errors => {return{ message:'ยืนยันรหัสผ่านไม่ถูกต้อง กรุณาลองใหม่'}}) }), login : Joi.object().keys({ email: Joi.string().email({ minDomainSegments: 2 }).required(), password:Joi.string().min(6).max(15).required() }) } module.exports = { validation, schema }
ในส่วนของฟังก์ชั่นการตรวจสอบความถูกต้องของข้อมูล validation() เราจะมีการใช้งานการกำหนดตัวแปร
ข้อมูลที่ส่งเข้ามา หรือ req.body ไว้ในตัวแปร user ซึ่งใช้กำหนดโดย res.locals.user และกำหนดค่า error กรณี
ไม่ผ่านการตรวจสอบความถูกต้องของข้อมูลไว้ในตัวแปร errors โดยใช้ res.locals.errors ค่าเหล่านี้ จะสามารถเรียก
ใช้งานหรือแสดงหน้า template view ที่ render ได้ สำหรับ template ที่เราจะ render ก็จะส่งมาในตัวแปร
req.renderPage
กระบวนทำงานของส่วนนี้คือ เมื่อผู้ใช้ทำการ submit ข้อมูล ก็จะมีการกำหนดหน้า render ไว้ในตัวแปร req.renderPage
สมมติเช่นหน้าล็อกอิน ก็จะเป็น req.renderPage = "pages/login" เป็นไฟล์ template หน้าล็อกอิน โดยเมื่อทำการ
ตรวจสอบความถูกต้องข้อมูลหรือก็คือเข้าฟังก์ชั่น validation() หากเกิด error ขึ้น เราก็จะทำการนำข้อความ error ที่กำหนด
ในตัวแปร res.locals.errors ไปแสดงในหน้า ล็อกอินเพื่อแจ้ง ตามตัวอย่างรูปด้านล่าง
นอกจากข้อความจากตัวแปร errors ที่เราส่งกลับมาแสดงในหน้าล็อกอิน แล้ว เรายังส่งค่าตัวแปร user ที่กำหนดในตัวแปร
res.locals.user มาใช้งานด้วย โดยเก็บค่า req.body ซึ่งเป็นข้อมูลที่ผู้ใช้กรอกเข้ามา เพื่อที่ว่ากรณี errors อย่างเช่นด้านบน
กรอกอีเมลถูกต้องสมบูรณ์แล้ว เราไม่อยากให้ผู้ใช้กรอกอีเมลใหม่ เราก็ใช้ค่าที่ส่งมาแสดงในส่วนของอีเมลได้เลย ผู้ใช้ก็กรอก
ในข้อมูลส่วนอื่นให้ถูกต้อง ไม่จำเป็นต้องกรอกข้อมูลใหม่ทั้งหมด
ในการกำหนด schema ให้กับรูปแบบการตรวจสอบข้อมูล ในที่นี้เรากำหนดไว้สองส่วนโดยแยกเป็น property ในรูปแบบ
const schema = { register : [รูปแบบสำหรับข้อมูลกรณีสมัครสมาชิก] login :[รูปแบบสำหรับข้อมูลกรณีล็อกอิน] }
เวลาใช่งานร่วมกับฟังก์ชั่นตรวจสอบ กรณีล็อกอิน / สมัครสมาชิก ก็ใช้เป็น validation(schema.login)
และ validation(schema.register) ตามลำดับ
ในการกำหนดข้อความแจ้งการตรวจสอบข้อมูลกรณี error ปกติเราสามารถใช้ค่าที่ Joi กำหนดมาให้เลยก็ได้ แต่ถ้า
หากเราต้องการกำหนดข้อความแจ้งเอง ก็สามารถใช้คำสั่ง
.error(errors => {return{ message:'ยืนยันรหัสผ่านไม่ถูกต้อง กรุณาลองใหม่'}})
ต่อท้ายส่วนที่ต้องการกำหนดข้อความแบบระบุเอง อย่างโค้ดด้นบน เรากำหนดข้อความให้กับ confirm_password กรณี
เกิด error ก็ใช้ค่านี้ต่อท้ายรูปแบบการตรวจสอบไป เป็น
confirm_password: Joi.any().valid(Joi.ref('password')).required() .error(errors => {return{ message:'ยืนยันรหัสผ่านไม่ถูกต้อง กรุณาลองใหม่'}})
หลังจากที่เราสร้างรูปแบบฟังก์ชั่นการตรวจสอบเป็น module เรียบร้อยแล้ว เราก็เรียกใช้งานในไฟล์ routers ในหน้า
ล็อกอิน และหน้าสมัครสมาชิก ดังนี้
ไฟล์ login.js [routes/login.js]
const express = require('express') const router = express.Router() const { validation, schema } = require('../validator/users') router.route('/') .all((req, res, next) => { // ตัวแปรที่กำหนดด้วย res.locals คือค่าจะส่งไปใช้งานใน template res.locals.pageData = { title:'Login Page' } // ค่าที่จะไปใช้งาน ฟอร์ม ใน template res.locals.user = { email:'', password:'' } // กำหนดหน้าที่ render กรณี error ไม่ผ่านการตรวจสอบข้อมูล req.renderPage = "pages/login" next() }) .get((req, res, next) => { res.render('pages/login') }) .post(validation(schema.login), (req, res, next) => { // ผ่านการรวจสอบ ลิ้งค์ไปหน้า /me res.redirect('/me') }) module.exports = router
ไฟล์ register.js [routes/register.js]
const express = require('express') const router = express.Router() const { validation, schema } = require('../validator/users') router.route('/') .all((req, res, next) => { // ตัวแปรที่กำหนดด้วย res.locals คือค่าจะส่งไปใช้งานใน template res.locals.pageData = { title:'Register Page' } // ค่าที่จะไปใช้งาน ฟอร์ม ใน template res.locals.user = { name:'', email:'', password:'', confirm_password:'' } // กำหนดหน้าที่ render กรณี error ไม่ผ่านการตรวจสอบข้อมูล req.renderPage = "pages/register" next() }) .get((req, res, next) => { res.render('pages/register') }) .post(validation(schema.register), (req, res, next) => { // เมื่อสมัครสมาชิก ผ่านการรวจสอบ ลิ้งค์ไปหน้า /me res.redirect('/me') }) module.exports = router
สำหรับในหน้า template ที่เกี่ยวข้อง เราก็จะมาปรับเพิ่มให้ส่วนของการแสดงข้อมูล errors ที่ส่งเข้ามา หาก
ไม่ผ่านการตรวจสอบ รวมทั้ง การนำค่าตัวแปร ข้อมูล user ที่ผู้ใช้กรอกแสดงในฟอร์มข้อมูล เริ่มต้นที่ส่วนของ errors
ก่อน
ไม่ว่าในไฟล์ login.ejs หรือ register.ejs เราก็จะใช้รูปแบบการแสดงเหมือนกันคือ แสดงข้อความแจ้งเตือน errors
ไว้ด้านบนของฟอร์ม ในรูปแบบ
<? if(typeof errors !== 'undefined'){ ?> <div class="form-group row"> <div class="col-7 mx-auto"> <div class="alert alert-warning alert-dismissible fade show" role="alert"> <?= errors.message ?> <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> </div> </div> <? } ?>
รูปแบบการแจ้งเตือนข้างต้น เป็นของ Bootstrap มีปุ่มกากบาท กดปิดได้ เราตรววจสอบก่อนว่า มีการ errors เกิดขึ้นไหม
เป็นค่าที่ได้จากคำสั่ง validation() เงื่อนการตรวจสอบคือ if(typeof errors !== 'undefined'){ ถ้ามีค่า หรือก็คือไม่ใช่
'undefined' ก็ให้แสดงข้อความ errors ผ่านตัวแปร <?= errors.message ?> ค่า errors นี้ได้จากการกำหนดในตัวแปร
res.locals.errors
ส่วนต่อมาเป็นส่วนของการกำหนดค่าเริ่มต้นให้กับข้อมูลในฟอร์ม หรือก็คือค่า value="" ของ form element ต่างๆ เช่น
<input type="password" class="form-control" name="password" value="<?= user.password ?>" placeholder="Your password">
เรากำหนด value="<?= user.password ?>" โดยใช้ค่า user ที่กำหนดโดย res.locals.user อย่างในกรณีข้างต้น ถ้า
ไม่มีการส่งข้อมูลเข้ามา ค่า user.password จะเท่ากับค่าว่าง ที่เรากำหนดในไฟล์ login.js [routes/login.js] แต่ถ้ามีการส่งค่าเข้ามา และอยู่ในขั้นตอนการตรวจสอบความถูกต้องของข้อมูล ที่ทำงานโดยฟังก์ชั่น validation() ค่านี้ก็จะเป็นค่าที่ผู้ใช้กรอก
ข้อมูลเข้ามา ตามที่เรากำหนดในไฟล์ users.js [validator/users.js] ในตัวแปร res.locals.user = req.body
เราจะได้ไฟล์ login.ejs และ register.ejs เป็นดังนี้
ไฟล์ login.ejs [views/pages/login.ejs]
<!doctype html> <html> <?- include('../partials/head') -?> <body> <?- include('../partials/header') -?> <?- include('../partials/nav') -?> <div class="container"> <h1 class="text-center">LOGIN</h1> <? if(typeof errors !== 'undefined'){ ?> <div class="form-group row"> <div class="col-7 mx-auto"> <div class="alert alert-warning alert-dismissible fade show" role="alert"> <?= errors.message ?> <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> </div> </div> <? } ?> <form class="mt-3" action="/login" method="POST" novalidate> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="email" class="form-control" name="email" value="<?= user.email ?>" placeholder="Your email"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="password" class="form-control" name="password" value="<?= user.password ?>" placeholder="Your password"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <button type="submit" class="btn btn-primary btn-block mx-auto"> Login</button> </div> </div> </form> </div> <?- include('../partials/footer') -?> </body> </html>
ไฟล์ register.ejs [views/pages/register.ejs]
<!doctype html> <html> <?- include('../partials/head') -?> <body> <?- include('../partials/header') -?> <?- include('../partials/nav') -?> <div class="container"> <h1 class="text-center">REGISTER</h1> <? if(typeof errors !== 'undefined'){ ?> <div class="form-group row"> <div class="col-7 mx-auto"> <div class="alert alert-warning alert-dismissible fade show" role="alert"> <?= errors.message ?> <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> </div> </div> <? } ?> <form class="mt-3" action="/register" method="POST" novalidate> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="text" class="form-control" name="name" value="<?= user.name ?>" placeholder="Your name"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="email" class="form-control" name="email" value="<?= user.email ?>" placeholder="Your email"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="password" class="form-control" name="password" value="<?= user.password ?>" placeholder="Your password"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <input type="password" class="form-control" name="confirm_password" value="<?= user.confirm_password ?>" placeholder="Confirm your password"> </div> </div> <div class="form-group row"> <div class="col-7 mx-auto"> <button type="submit" class="btn btn-primary btn-block mx-auto"> Register</button> </div> </div> </form> </div> <?- include('../partials/footer') -?> </body> </html>
เป็นอันเรียบร้อยในขั้นตอนการเตรียมหน้าตาเว็บเพจส่วนต่างๆ ที่เกี่ยวกับระบบสมาชิก ซึ่งเราได้รู้จักกับการใช้งาน
EJS Template layout ถ้าหากเราต้องการสร้างหน้าเพจใดๆ เพิ่ม เราก็สามารถสร้างไฟล์ template ไว้ในโฟลเดอร์
"views/pages" หรือจะปรับแต่งส่วนเพิ่มเติมต่างๆ ใน "views/partials" ตามต้องการก็ได้
นอกจากนั้น เราได้ทำความรู้จักกับการใช้งาน Joi Validation เพิ่มเติม ซึ่งในบทความก่อนหน้า เป็นการใช้งานการ
ตรวจสอบข้อมูลกรณีใช้งานกับ API การคืนค่า ก็จะเป็นอีกแบบหนึ่ง เพราะกรณีนั้น เป็นการคืนค่าออกไปทางผู้ใช้ในรูปแบบ
json data แต่กรณีนี้ เป็นการคืนค่าออกมาและนำไปใช้งานร่วมกับ template
เนื้อหาในตอนหน้า เราจะมาส่วนทีสำคัญอีกส่วน ที่เกี่ยวข้องกับระบบสมาชิก นั่นก็คือ cookie และก็ session เข้าใจ
อย่างง่ายเบื้องต้นคือ cookie คือการเก็บข้อมูลไว้ที่ฝั่งผู้ใช้ ส่วน session คือการเก็บข้อมูลที่ฝั่ง server