การกำหนด EJS Template Layout ใน Express สำหรับ ระบบสมาชิก

เขียนเมื่อ 4 ปีก่อน โดย Ninenik Narkdee
template layout expressjs ejs nodejs

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ template layout expressjs ejs nodejs

ดูแล้ว 11,986 ครั้ง


อย่างที่ได้เกริ่นไปแล้วในเนื้อหาในตอนที่ผ่านมาว่า เราจะ
ทำระบบเกี่ยวกับสมาชิก ซึ่งระบบสมาชิกนั้น จะมีองค์ประกอบมากมาย
ที่จะเข้ามาเกี่ยวข้อง บางส่วนเราอาจจะได้รู้จัก และทำความเข้าใจเบื้องต้น
ไปบ้างแล้ว เช่น 
    การใช้งาน 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">&times;</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">&times;</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">&times;</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  


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง











เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ