สอนทำเว็บไซต์ด้วย Node.js, Express และ MongoDB ตอนที่ 5 - ลองหัดใช้ Template Engine ชื่อ Pug

Published on
NodeJS
2020/02/web-development-with-nodejs-mongodb-part5
Discord

สวัสดีครับ มาต่อกันที่ตอนที่ 5 กันนะครับ วันนี้จะมาพูดถึงเรื่องของ Template Engine กัน หลังจากที่ตอนนี้เราสามารถทำเว็บด้วย Express.js กันได้แล้ว และสามารถแสดงข้อความ แสดง String หรือ HTML ได้แล้ว ต่อมา เราจะใช้การ render file html และการ render file อื่นๆ ครับ

เนื้อหาบทเรียน


Getting Started

บทนี้เราจะมาทำเว็บจริงๆกันละครับ โดยใช้ Pug ครับ ซึ่ง Pug นั้นเป็น Template Engine ครับ ถ้าเราไม่ใช่ Pug จริงๆแล้วมันจะมี Template Engine อื่นๆ อีกเต็มไปหมด เช่น EJS, Handlebars อะไรพวกนี้ครับ

และถ้าถามว่าไม่ใช่ Template Engine ได้มั้ย? ก็ตอบว่าได้ครับ เป็นไฟล์ HTML ธรรมดานี่แหละ

แต่ Template Engine มันมีข้อดีกว่า HTML หลายข้อเลย

ข้อดีของ Template Engine

  • เราสามารถแบ่ง Layout ได้ ว่าจะแยก Header, Footer หรือ Content ได้ คล้ายๆการแบ่ง Component ซึ่งถ้าเป้น HTML เราก็ต้องก็อปปี้โค๊ดทั้งหมด
  • เราสามารถรับ / ส่งข้อมูลที่เป็นตัวแปร JavaScript ที่ฝั่ง Server ได้โดยตรง (ถ้าเป็น html เราอาจจะต้อง assign ผ่าน window (global scope) / localStorage หรือ AJAX แล้วแต่)
  • ส่วนใหญ่ สามารถเขียน JavaScript หรือ function ลงไปได้ เช่น การ loop สมมติเรามีรายชื่อเยอะๆ ถ้า HTML เราก็ต้องนั่งก็อปทั้งหมด แต่ template engine เราก็แค่วนลูป เพื่อแสดง
  • ประหยัดเวลา เนื่องจากเรา Reuse code ได้นั่นเอง

Pug คืออะไร?

Pug ก็เป็น Template Engine ตัวนึง ที่ข้อดีของมันคือ มันสั้น กระชับ แรกๆ อาจจะงงๆ Syntax ของมัน แต่พอใช้ไปก็จะชินครับผม (ก่อนหน้านี้ ถ้าใครเคยได้ยิน หรืออาจจะเคยเห็น เมื่อก่อนมันชื่อ Jade Template ครับ แต่มีปัญหาเรื่อง Trademark ก็เลยเปลี่ยนชื่อเป็น Pug)

การที่เราจะใช้ Pug เราจะใช้คู่กับ Express ครับ เลยทำให้ Pug มันกลายเป็น Server side template engine นั่นเอง

โดยที่ Pug มันจะแปลง syntax ของมันให้เป็น HTML ตอน render ครับ ตัวอย่างเช่น (เดี๋ยวอธิบายเพิ่ม)

h1 Hello

มันจะแปลงเป็น html แบบนี้

<h1>Hello</h1>

เริ่มสร้างโปรเจ็ค

เรามาเริ่มต้นสร้างโปรเจ็คขึ้นมาครับ ด้วย npm init -y เหมือนเดิม แบบรีบๆ จากนั้นสร้างไฟล์ app.js ขึ้นมา ดังนี้

const express = require('express')

const app = express()

app.get('/', function (req, res) {
  res.send('Hello World')
})

app.listen(3000)

ติดตั้ง express และ pug ผ่าน npm รอไว้ก่อน

npm install express pug

ก่อนไปพูพถึงเรื่อง Pug ขอพูดการ serve html ก่อนละกันครับ ปกติเราสามารถ render file html ผ่าน Express ด้วยการใช้ res.render() ครับ เช่น

app.get('/about', (req, res) => {
  res.render('about')
})

ตัว Express มันก็จะไปอ่านไฟล์ชื่อ about.xxx ที่เรากำหนดเป็น view folder (และ xxx คือ extensions ที่เรากำหนด โดยถ้าไม่กำหนดมันก็จะเป็น about.html นั่นเอง)

ลอง start server และถ้าเราเข้าเว็บ http://localhost:3000/about/

จะได้ข้อความแบบนี้

Error: No default engine was specified and no extension was provided.

เราต้องทำการ กำหนด template engine และ extension ครับ ทำได้ด้วยการเพิ่ม เพื่อบอก Express ว่าไฟล์ของเราจะอยู่ที่โฟลเดอร์ views นะ (__dirname + '/views)

app.set('views', __dirname + '/views')

// หรือจะแบบนี้ก็ได้
// app.use(express.static('views'));

// หรือ
// app.use('views', path.join(__dirname, 'views'))

และก็ set template engine ซะ

app.set('view engine', 'pug')

จากนั้น เพิ่มไฟล์ about.pug ในโฟลเดอร์ views ไม่มีสร้างใหม่เลย ที่มีเนื้อหาแค่

h1 Hello World

ลอง stop และ start server ใหม่อีกครั้ง เราจะได้หน้าเว็บที่มี Hello World เป็นแบบ h1 ครับ

นอกจากนี้เราก็สามารถส่งค่า Object เพื่อให้ Pug มันแสดงผลได้เช่น

app.get('/about', (req, res) => {
  res.render('about', {
    message: 'This is message sent from app.js'
  })
})

จากนั้นไฟล์ views/about.pug ก็เพิ่มนี้ลงไปครับ

h1 Hello World
p= message
p This is still valid #{message}

ก่อนไปดู Syntax Pug ตอนนี้ไฟล์ app.js เราเป็นแบบนี้

const express = require('express')

const app = express()

// app.set('views', __dirname + '/views');

app.use(express.static('views'))
app.set('view engine', 'pug')

app.get('/', function (req, res) {
  res.send('Hello World')
})

app.get('/about', (req, res) => {
  res.render('about', {
    message: 'This is sent from app.js file'
  })
})

app.listen(3000)

เราสามารถ start server และลอง View Source ดูได้ครับ จะเห็นเป็นแบบนี้

<h1>Hello World</h1>
<p>This is sent from app.js file</p>
<p>This is still valid This is sent from app.js file</p>

เพราะ Pug มันแปลงให้นั่นเอง

Pug Syntax

เรามาเรียนรู้ Syntax ของ Pug กันดีกว่าครับ เริ่มจาก

การใส่ id และ class ให้กับ element ใช้แบบนี้ (ใช้ # และ .)

p#myId
p.myClass

จะได้เป็น

<p id="myId"></p>
<p class="myClass"></p>

Doctype ก็ง่ายๆ ด้วยคำสั่ง

doctype html

ปกติ HTML เรามองเป็น Tree อยู่แล้วใช่มั้ยครับ ใน Pug ก็ให้เรามองเป็น Tree มองเป็น Root Element ไล่ไป Parent -> Child โดยใช้ space / tab เป็นตัวกำหนด

ตัวอย่างเช่น เรามี div ข้างในมี ul และ li เป็น item จะได้ดังนี้

div
  ul
    li This is item
    li This is item 2

มันจะแปลงเป็น html แบบนี้

<div>
  <ul>
    <li>This is item</li>
    <li>This is item 2</li>
  </ul>
</div>

สิ่งที่ต้องระวังสำหรับ Pug คือ เราห้ามผสม indent ระหว่าง space กับ tab โดยเด็ดขาด และถ้าใช้การเว้นวรรค space 2 เราก็ต้องให้เหมือนกันหมด ห้ามมี 2 บ้าง 3 บ้าง 4 บ้างเด็ดขาดครับ

เราสามารถ สร้างเงื่อนไข เพื่อให้มัน render ได้ เช่น

if name
  h2 Ahoy! #{name}
else
  h2 Hello

การ assign ตัวแปรใส่ element (name คือตัวแปร ที่เราส่งมาจาก Express นะครับ ไม่ใช่ string)

p= name

ซึ่งปกติ ถ้าเป็น element และ body เราจะเขียนแค่ p และเว้นวรรคแบบนี้

p Hello

หรือถ้าจะผสมระหว่างตัวแปร และ string ก็แบบนี้

p Hello #{name}

เราสามารถวนลูปได้เช่นกัน

ul
  each user in users
    li= user.name

Pug แบบเร็วๆ

สำหรับคนที่พอเข้าใจ Syntax แล้ว อันนี้คือแบบเร็วๆ สำหรับคนขี้เกียจอ่านครับ

// comment เวลา render จะเป็น <!-- -->

//- comment แบบนี้ จะไม่แสดงผล

//- tag <img />
img

//- tag <img src="path/to/image.png" alt="Alt" />
img(src="path/to/image" alt="Alt")

//- <a href="https://devahoy.com">Devahoy</a>
a(href="https://devahoy.com") Devahoy

//- <input type="checkbox" name="agreement" checked="checked" />
input(
  type='checkbox'
  name='agreement'
  checked
)

//- div ไม่จำเป็นต้องพิมพ์ก็ได้ถ้าเราจะกำหนดคลาส หรือ id เช่น
//- <div class="container"></div>
.container
//- มีค่าเท่ากับ
div.container

//- <link rel="stylesheet" href="/css/style.css">
link(href='/css/style.css', rel='stylesheet')

//- <script src="/path/to/jquery.js"></script>
script(src='/path/to/jquery.js')

Layout และ Template

ลองสร้างเว็บแบบนี้หลายๆ หน้า เช่น About, Home Page, Contact, Portfolio, Service ซึ่งเราอาจจะมี html ที่คล้ายๆกัน เช่น มีการกำหนด meta ใน head มีการใส่ stylesheet หรือ javascript ยิ่งเรามีหน้าเยอะ เราก็ต้องมาก็อปปี้โค๊ดใส่ทุกๆหน้า

<DOCTYPE html>
  <html>
    <head></head>
    <body></body></html
></DOCTYPE>

สมมติ เรานึกได้ ต้องเพิ่ม Footer ให้เหมือนกันอีก ก็ต้องมาแก้ 6 ที่ (สมมติเว็บมี 6 หน้า) วันดีคืนดี อยากเพิ่มส่วน Header หรือแก้ไข wording ที่ Footer ก็ต้องมาแก้ 6 ที่เลย

แล้วถ้าเรามี Template ตัวกลางละ เราก็แก้แค่ที่เดียว ส่วนไหนที่มันเหมือนกัน เราก็ Reuse ได้ ก็เลยเป็นที่มาของ Layout และ Template นั่นเอง

Layout

เราจะใช้ block และ extends กันครับ

ตัวอย่างการใช้ Layout ดังนี้ ผมตั้งชื่อมันว่า views/layout.pug

html
  head
  title My Web with Pug
  link(rel='stylesheet', href='/style.css')
  body
    .container
      block content
    .footer
      p This is footer

สังเกตเห็นว่า เรามี block content ตรงนี้มันจะเป็นการแสดงผลส่วน content ครับ ตัวอย่างการใช้งานคือ เราสร้างไฟล์อีกไฟล์นึงชื่อ index.pug ละกัน

extends layout.pug

block content
  h1 This is index.pug
  p Hello World

ไฟล์นี้ เราทำการ extends หมายความว่าใช้โค๊ดแบบเดียวกันกับ layout.pug แต่แตกต่างกัน ตรงนี้ block content เรามี h1 และ p ซึ่งตรงนี้ มันจะไปถูก insert ใส่ block content ของไฟล์ layout นั่นเอง ซึ่งพอเป็นงี้ ไฟล์ index.pug มันจะมีค่าแบบนี้

html
  head
  title My Web with Pug
  link(rel='stylesheet', href='/style.css')
  body
    .container
      h1 This is index.pug
      p Hello World
    .footer
      p This is footer

ต่อมาการ Include หมายถึงเราสามารถ Reuse บางส่วนมาใช้ได้ ไม่ต้อง extends ทั้งหมด แบบ layout.pug เช่น เรามี ป้ายโฆษณาอยู่ และถ้าเราอยากเอาโฆษณาไปใช้ เราก็ต้องก็อปโค๊ดเดิมๆ ซ้ำๆใช่มั้ย จะแก้ที ก็ต้องแก้ทุกที่ เรา reuse ได้ด้วยการสร้างไฟล์ views/ads.pug ขึ้นมา

.footer
  .container
    .row
      .col-12
        h2 50% Off
        p ลดราคา 50% ซื้อเดี๋ยวนี้!

ทีนี้ เราอยากให้หน้าไหนมีโฆษณา เราก็เพียงแค่ใช้ include ครับ ตัวอย่างเช่นไฟล์ index.pug เราทำการเพิ่ม ads.pug ไปในส่วน block content นั่นเอง

extends layout.pug

block content
  h1 This is index.pug
  p Hello World
  include ads.pug

ก็จะเห็นว่าเราใช้ Pug มันทำให้เราเขียนเว็บได้สะดวกขึ้น สามารถเรียกตัวแปร ทำการวนลูป สร้าง Layout ทำการ Reuse code ได้ดีกว่า HTML เพียวๆ อีก จริงๆแล้วตัว Pug ก็ยังมีรายละเอียดอีกเยอะที่เพื่อนๆสามารถไปอ่านเพิ่มเติมได้ครับ บทความนี้ก็เป็น Overview ให้เห็นภาพแล้วกันเนาะ ยังไงเราก็ต้องไปฝึก ลองเล่น ลองใช้งานจริงกันดูครับ หรือลองดูตัวอย่างไฟล์ Source Code ของผมได้เลยครับ

Reference

อ่านเรื่อง Pug เพิ่มเติมได้ครับ https://pugjs.org/language/attributes.html

สุดท้ายนี้ หากใครติดปัญหาตรงส่วนไหน สามารถสอบถามเข้ามาได้ตลอดครับ ไว้เจอกันบทความถัดๆไปนะครับ สำหรับซีรีย์นี้ยังอีกยาวไกล รอติดตามตอนต่อไปครับ และหลังจากนี้ผมจะทำเป็น Video Tutorial ลงบน Youtube ไว้ด้วยครับ เผื่อบางคนชอบแบบดูวิดีโอมากว่า ก็รอติดตาม หรือไปกด Subscribe ที่ช่อง Youtube ผมไว้ได้นะครับ

ส่วน Source Code (อยู่ part5) สามารถเข้าไปดาวน์โหลด หรือ clone ผ่าน Github ได้เลย หากใครไม่รู้จัก Git สามารถอ่านบทความนี้เพิ่มได้ครับ Git คืออะไร ? + พร้อมสอนใช้งาน Git และ Github

ขอบคุณครับ

❤️ Happy Coding

Buy Me A Coffee
Authors
Discord