Why template than nothing - EJS

Published at: 2020-07-03 16:00:00

Updated at: 2023-10-01 13:59:37

templateejs

Hi guys! Today's article, I'm going to explain to you why should you implement the use of Templating than just use the bare bone of HTML. How can it basically change your code while very much able to help you a lot in some ways that you won't understand until you actually implement and use it yourself.

At first when learning how to code JavaScript programming I thought I could code it as Vanilla JavaScript without needing to implement other front-end framework. At least until I have very broad grasp of how Vanilla JavaScript works. So I did continue till now making and building my own portfolio website just using Vanilla JavaScript together with JavaScript based back-end framework like Node JS. I started realizing when I'm in the middle of creating my second version of my portfolio. Specifically when trying to add a blog feature. There's no problem when trying to display all blog post available that I have in my database. But unfortunately, I faced a problem when trying to display a single blog post page.

But before we begin, let me inform you I did too, provide a video sharing session regarding this issue. If you find it much easier to follow me through video form. Then I would suggest you to go to this link. But for those who are more comfortable following in written form. You are welcome to continue doing so. So, with that out of the way. Let's start coding!

Coding Start

Let me explain how it goes through sample code. Let's say we want to display all available blog posts. And then when user click on read more, we want the system to maneuver or basically bring the user to the single blog post page. So in this example I'm going to use just Vanilla JavaScript for the front-end. Node JS as the back-end framework and just a simple object of blog posts act as our data storage. Then, later on we going to change the code to implement a template.

A. Without Template Usage

So first, let's create the route to display all blogs available in our database. But before that, make sure you install these packages first:-

  1. Express
  2. Cors
  • index.js:-
/* Dependencies */
// Express Package
const express = require('express')
const app = express()
// Cors
const cors = require('cors')
// Path
const path = require('path')
 
/* Middlewares */
// cors
app.use(cors())
// body parser
app.use(express.json())
// Static folder
app.use(express.static('web'))
 
/* Gimmick Data Storage */
let posts = [
    {
        id: 1,
        title: 'Post 1',
        description: 'Post One Description'
    },
    {
        id: 2,
        title: 'Post 2',
        description: 'Post Two Description'
    },
    {
        id: 3,
        title: 'Post 3',
        description: 'Post Three Description'
    }
]
 
/* Routes */
// 1.0 - page to display All Blog Posts Available
app.get('/blog', async(req, res) => {
    // using just NodeJS won't enable use to render a page and send data together when rendering (in my knowledge)
    // so the only way to do it, its just render a static html page for now
    // - using 'path resolve' to get the path of where 'blog.html' situated
    let parentPath = path.resolve(__dirname + '/blog.html', '../')
    // - using 'sendFile' to render a static html page of 'blog.html'
    res.sendFile(path.join(parentPath + '/views/blog.html'))
})
 
/* Server Connection */
app.listen(5000, console.log('Server is up and running at port 5000.'))

You might already notice that we don't actually send any blog post data right? That's because with only using just Node JS without implementing or applying any other third party package. Node JS not able to render a page dynamically. The only possible way for us to render a page is by using Node JS sendFile which is so limited and unable to send data while rendering a page. With the above code, we are only able to serve the page of where the content will be. So where exactly we are able to get our desired blog post data? Let me show you.

  • blog.html (Page to display All Blog Post Available):-
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blog</title>
</head>
<body>
    <div class="container"></div>
 
    <script src="/js/blog.js"></script>
</body>
</html>
  • blog.js (Script to handle fetching of All Blog Post Available):-
// create a function to handle data fetching
let dataFromDB = async() => {
    // fetch data (All Blog Posts Available) through API URL of '/api/blog'
    const res = await fetch('/api/blog')
    const json = await res.json()
 
    if(json.message === 'Successful!') {
        // update into UI
        let posts, htmlTemplate
 
        posts = json.data
 
        posts.forEach(post => {
            htmlTemplate = `
                <div class="post" id="post-${post.id}">
                    <h1 class="title">${post.title}</h1>
                    <p class="desc">${post.description}</p>
                    /* will bring user to route of '/blog/id' */
                    <a href="/blog/${post.id}">Read More</a>
                </div>
            `
 
            // append the template to UI
            document.querySelector('.container').insertAdjacentHTML('beforeend', htmlTemplate)
        });
    } else {
        alert('No data fetched from db!')
    }
}
 
dataFromDB()

As you see, we actually able to get data (All Blog Posts Available) by using JavaScript fetch from the front-end of the system. This is the only way how we can get data from our server or back-end. The only next thing we could do is to create those API URL of route /api/blog to send/return the data we need like shown below:-

  • index.js:-
/* Dependencies */
// Express Package
const express = require('express')
const app = express()
// Cors
const cors = require('cors')
// Path
const path = require('path')
 
/* Middlewares */
// cors
app.use(cors())
// body parser
app.use(express.json())
// Static folder
app.use(express.static('web'))
 
/* Gimmick Data Storage */
let posts = [
    {
        id: 1,
        title: 'Post 1',
        description: 'Post One Description'
    },
    {
        id: 2,
        title: 'Post 2',
        description: 'Post Two Description'
    },
    {
        id: 3,
        title: 'Post 3',
        description: 'Post Three Description'
    }
]
 
/* Routes */
// 1.0 - page to display All Blog Posts Available
app.get('/blog', async(req, res) => {
    // using just NodeJS won't enable use to render a page and send data together when rendering (in my knowledge)
    // so the only way to do it, its just render a static html page for now
    // - using 'path resolve' to get the path of where 'blog.html' situated
    let parentPath = path.resolve(__dirname + '/blog.html', '../')
    // - using 'sendFile' to render a static html page of 'blog.html'
    res.sendFile(path.join(parentPath + '/views/blog.html'))
})
// 1.1 - route to handle passing of All Blog Posts Available
app.get('/api/blog', async(req, res) => {
    try {
        // send All Blog Posts Available
        res.json({ message: 'Successful!', data: posts })
    } catch(err) {
        res.json({ message: 'Unsuccessful!', data: err })
    }
})
 
/* Server Connection */
app.listen(5000, console.log('Server is up and running at port 5000.'))

Up till now, there was no problem to serve our Blog page using this method. But the problem you will definitely face when continuing doing so, is when you want to serve a Single Blog Post page that user will clicked on. Let's create a route to handle the single blog post page.

  • index.js:-
/* Dependencies */
// Express Package
const express = require('express')
const app = express()
// Cors
const cors = require('cors')
// Path
const path = require('path')
 
/* Middlewares */
// cors
app.use(cors())
// body parser
app.use(express.json())
// Static folder
app.use(express.static('web'))
 
/* Gimmick Data Storage */
let posts = [
    {
        id: 1,
        title: 'Post 1',
        description: 'Post One Description'
    },
    {
        id: 2,
        title: 'Post 2',
        description: 'Post Two Description'
    },
    {
        id: 3,
        title: 'Post 3',
        description: 'Post Three Description'
    }
]
 
/* Routes */
// 1.0 - page to display All Blog Posts Available
app.get('/blog', async(req, res) => {
    // using just NodeJS won't enable use to render a page and send data together when rendering (in my knowledge)
    // so the only way to do it, its just render a static html page for now
    // - using 'path resolve' to get the path of where 'blog.html' situated
    let parentPath = path.resolve(__dirname + '/blog.html', '../')
    // - using 'sendFile' to render a static html page of 'blog.html'
    res.sendFile(path.join(parentPath + '/views/blog.html'))
})
// 1.1 - route to handle passing of All Blog Posts Available
app.get('/api/blog', async(req, res) => {
    try {
        // send All Blog Posts Available
        res.json({ message: 'Successful!', data: posts })
    } catch(err) {
        res.json({ message: 'Unsuccessful!', data: err })
    }
})
 
// 2.0 - page to display Single Blog Post
app.get('/blog/:id', async(req, res) => {
    // send user according to which Single Blog Post they clicked on
    // - send user to Single Blog Post of 'id = 1'
    if(+req.params.id === 1) {
        // - using 'path resolve' to get the path of where 'singleBlog1.html' situated
        let parentPath = path.resolve(__dirname + '/singleBlog1.html', '../')
        // - using 'sendFile' to render a static html page of 'singleBlog1.html'
        res.sendFile(path.join(parentPath + '/views/singleBlog1.html'))
    } else if(+req.params.id === 2) { // - send user to Single Blog Post of 'id = 2'
        // - using 'path resolve' to get the path of where 'singleBlog2.html' situated
        let parentPath = path.resolve(__dirname + '/singleBlog2.html', '../')
        // - using 'sendFile' to render a static html page of 'singleBlog2.html'
        res.sendFile(path.join(parentPath + '/views/singleBlog2.html'))
    } else if(+req.params.id === 3) { // - send user to Single Blog Post of 'id = 3'
        // - using 'path resolve' to get the path of where 'singleBlog3.html' situated
        let parentPath = path.resolve(__dirname + '/singleBlog3.html', '../')
        // - using 'sendFile' to render a static html page of 'singleBlog3.html'
        res.sendFile(path.join(parentPath + '/views/singleBlog3.html'))
    } else {
        // console log error message
        console.log(`There's no post with the id of ${req.params.id} exist in our database.`)
    }
})
 
/* Server Connection */
app.listen(5000, console.log('Server is up and running at port 5000.'))

Like shown above, we have no way of doing this dynamically. Unfortunately we have to create a different single blog post page for each and every of our post or new added post in the future. But, the problems still not end there yet tho. Continue on below:-

  • singleBlog1.html (Page to display Single Blog Post of ID = 1):-
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blog</title>
</head>
<body>
    <div class="container"></div>
 
    <script src="/js/singleBlog1.js"></script>
</body>
</html>
  • singleBlog1.js (Script to handle fetching of Single Blog Post of ID = 1);-
// create a function to handle data fetching
let dataFromDB = async() => {
    // fetch data (All Blog Posts Available) through API URL of '/api/blog/1'
    const res = await fetch('/api/blog/1')
    const json = await res.json()
 
    if(json.message === 'Successful!') {
        // update into UI
        let posts, htmlTemplate
 
        post = json.data
 
        htmlTemplate = `
            <div class="post" id="post-${post.id}">
                <h1 class="title">${post.title}</h1>
                <p class="desc">${post.description}</p>
            </div>
        `
 
        // append the template to UI
        document.querySelector('.container').insertAdjacentHTML('beforeend', htmlTemplate)
    } else {
        alert('No data fetched from db!')
    }
}
 
dataFromDB()

With the example above, this also means that we need a separate JavaScript scripting for each singleBlog.html we've created. And in our index.js, we need to add one new route to handle the sending of data to each singleBlog.js we've created like shown below:-

  • index.js:-
/* Dependencies */
// Express Package
const express = require('express')
const app = express()
// Cors
const cors = require('cors')
// Path
const path = require('path')
 
/* Middlewares */
// cors
app.use(cors())
// body parser
app.use(express.json())
// Static folder
app.use(express.static('web'))
 
/* Gimmick Data Storage */
let posts = [
    {
        id: 1,
        title: 'Post 1',
        description: 'Post One Description'
    },
    {
        id: 2,
        title: 'Post 2',
        description: 'Post Two Description'
    },
    {
        id: 3,
        title: 'Post 3',
        description: 'Post Three Description'
    }
]
 
/* Routes */
// 1.0 - page to display All Blog Posts Available
app.get('/blog', async(req, res) => {
    // using just NodeJS won't enable use to render a page and send data together when rendering (in my knowledge)
    // so the only way to do it, its just render a static html page for now
    // - using 'path resolve' to get the path of where 'blog.html' situated
    let parentPath = path.resolve(__dirname + '/blog.html', '../')
    // - using 'sendFile' to render a static html page of 'blog.html'
    res.sendFile(path.join(parentPath + '/views/blog.html'))
})
// 1.1 - route to handle passing of All Blog Posts Available
app.get('/api/blog', async(req, res) => {
    try {
        // send All Blog Posts Available
        res.json({ message: 'Successful!', data: posts })
    } catch(err) {
        res.json({ message: 'Unsuccessful!', data: err })
    }
})
 
// 2.0 - page to display Single Blog Post
app.get('/blog/:id', async(req, res) => {
    // send user according to which Single Blog Post they clicked on
    // - send user to Single Blog Post of 'id = 1'
    if(+req.params.id === 1) {
        // - using 'path resolve' to get the path of where 'singleBlog1.html' situated
        let parentPath = path.resolve(__dirname + '/singleBlog1.html', '../')
        // - using 'sendFile' to render a static html page of 'singleBlog1.html'
        res.sendFile(path.join(parentPath + '/views/singleBlog1.html'))
    } else if(+req.params.id === 2) { // - send user to Single Blog Post of 'id = 2'
        // - using 'path resolve' to get the path of where 'singleBlog2.html' situated
        let parentPath = path.resolve(__dirname + '/singleBlog2.html', '../')
        // - using 'sendFile' to render a static html page of 'singleBlog2.html'
        res.sendFile(path.join(parentPath + '/views/singleBlog2.html'))
    } else if(+req.params.id === 3) { // - send user to Single Blog Post of 'id = 3'
        // - using 'path resolve' to get the path of where 'singleBlog3.html' situated
        let parentPath = path.resolve(__dirname + '/singleBlog3.html', '../')
        // - using 'sendFile' to render a static html page of 'singleBlog3.html'
        res.sendFile(path.join(parentPath + '/views/singleBlog3.html'))
    } else {
        // console log error message
        console.log(`There's no post with the id of ${req.params.id} exist in our database.`)
    }
})
// 2.1 - route to handle passing of Single Blog Post user requested
app.get('/api/blog/:id', async(req, res) => {
    // send user Single Post data according to which user have requested
    try {
        // - send user Single Blog Post data of 'id = 1'
        if(+req.params.id === 1) {
            res.json({ message: 'Successful!', data: posts[0] })
        } else if(+req.params.id === 2) { // - send user Single Blog Post data of 'id = 2'
            res.json({ message: 'Successful!', data: posts[1] })
        } else if(+req.params.id === 3) { // - send user Single Blog Post data of 'id = 3'
            res.json({ message: 'Successful!', data: posts[2] })
        } else {
            // console log error message
            console.log(`There's no post with the id of ${req.params.id} exist in our database.`)
        }
    } catch(err) {
        // - send user Single Blog Post's error message
        res.json({ message: 'Unsuccessful!', data: err })
    }
})
 
/* Server Connection */
app.listen(5000, console.log('Server is up and running at port 5000.'))

Like I said earlier, this is the only way to do it now when just using bare Node JS. But don't worry tho. I'm going to show you how we can change our code, not much, just a tad actually, with just using a package called EJS (Embedded JavaScript Templating). Let's get started.

B. With Template Usage

  • index.js:-
/* Dependencies */
// Express Package
const express = require('express')
const app = express()
// Cors
const cors = require('cors')
// Path
const path = require('path')
 
/* Middlewares */
// cors
app.use(cors())
// body parser
app.use(express.json())
// Static folder
app.use(express.static('web'))
 
/* Gimmick Data Storage */
let posts = [
    {
        id: 1,
        title: 'Post 1',
        description: 'Post One Description'
    },
    {
        id: 2,
        title: 'Post 2',
        description: 'Post Two Description'
    },
    {
        id: 3,
        title: 'Post 3',
        description: 'Post Three Description'
    }
]
 
/* Routes */
// 1.0 - page to display & data to send of All Blog Posts Available
app.get('/blog', async(req, res) => {
    // dynamically render a page while in the same time send data together with it
    // - make sure to change your extension of 'blog.html' to 'blog.ejs' in order to make it work
    try {
        res.render('blog', { message: 'Successful!', data: posts })
    } catch(err) {
        res.render('blog', { message: 'Unsuccessful!', data: err })
    }
})
 
// 2.0 - page to display & data to send of Single Blog Post
app.get('/blog/:id', async(req, res) => {
    // filter which sigle post to send to singleBlog page
    let post = posts.filter(post => post.id === +req.params.id)
 
    // dynamically render a page while in the same time send data together with it
    // - make sure to change your extension of 'singleBlog.html' to 'singleBlog.ejs' in order to make it work
    try {
        res.render('singleBlog', { message: 'Successful!', data: post })
    } catch(err) {
        res.render('singleBlog', { message: 'Unsuccessful!', data: err })
    }
})
 
/* Server Connection */
app.listen(5000, console.log('Server is up and running at port 5000.'))
  • blog.ejs:-
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blog</title>
</head>
<body>
    <div class="container">
        <% data.forEach(post => { %>
            <div class="post" id="post-<%= post.id %>">
                <h1 class="title"><%= post.title %></h1>
                <p class="desc"><%= post.description %></p>
                /* will bring user to route of '/blog/id' */
                <a href="/blog/<%= post.id %>">Read More</a>
            </div>
        <% }) %>  
    </div>  
</body>
</html>
  • singleBlog.ejs (Previously as singleBlog1.html or singleBlog2.html or so forth):-
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blog</title>
</head>
<body>
    <div class="container">
        <% data.forEach(post => { %>
            <div class="post" id="post-<%= post.id %>">
                <h1 class="title"><%= post.title %></h1>
                <p class="desc"><%= post.description %></p>
            </div>
        <% }) %>  
    </div>  
</body>
</html>

Ain't that more simple? With this we able to cut our code with only having:-

  1. 2 routes only in our index.js
  2. just a Single Blog post page, singleBlog.ejs
  3. no need to apply dedicated & separated script anymore

There you have it. We are able to overcome our problems of manually create every single blog post page that available with just using a template. When I just started learning to code using Node JS. Like I said earlier, I thought I could do it even without having to use any third party packages. But after a while learning and implement all sort of codes to build or make my own website (Portfolio). I started knowing that at some point, I need to use any third party packages to make my Portfolio to work as intended. And soon definitely without a doubt, I need to learn how to use a front-end framework too.

So guys, I hope what I share with you guys today able to at least help you to understand Node JS and the use of templating much better. This does not means that you should not learn Vanilla JavaScript and can straightaway learn how to use Front-end Framework. It depends actually. But, my recommendation will be to start learning the basic stuff first and progress more along the way. Create a website of your own. You will not believe how much and how fast you keep learning new stuff when doing so. It's the most effective way for me, personally.

Anyway, like always, thanks for being here. until next time. Chao! Salam.