Edward's Tech Site this site made with Next.js 13 , see the code HOWTO: Jun 20, 2022 - EJS
Create a Node/Express/EJS site that displays various types of data
this howto shows you how to use the templating engine EJS to make multipage Node/Express server-side web sites the EJS site we will build in this howto is based on this site: edwardtanguay.netlify.app/howtos?id=566 this site looks like this: analyze the code of this site it has an MVC patternthe control (server.js) gets data from the module which fetches data from various sources (external API, local JSON file, Excel file, directory of images, and directory of md files) this data is sent as an object to the view which displays the data note that the view uses simple ES6 template string syntax problem: while the above ES6 template string syntax can be a decent solution for simple one-page sites and can go a long way even if you have a nav bar with server-side pages, using a template engine solves many problems you would have to solve latersolution: the most popular server-side template engine for Node/Express sites is called EJS (i.e. E mbedded J avaS cript templates) create new directory e.g. howto-node-express-ejs-mvc-site
and copy in site from the above site's repository swap out the template string syntax for EJS templating set up basic EJS plumbing:since this site has an MVC structure, we are essentially swapping out the V iew format, from ES6-template-strings to EJS npm i ejs
create public/views/pages/index.ejs
in server.jsimport path from 'path';
const __dirname = path.resolve(path.dirname(''));
app.set('view engine', 'ejs');
app.et('views', path.join(__dirname, './public/views'));
app.get('/', (req, res) => {
res.render('pages/index')
});
send in model data to the view as we did beforeserver.jsres.render('pages/index', siteData)
and display it in the viewindex.ejstest of <b>ejs</b>
<hr />
there are <%=nouns.length%> nouns
the site should now look like this: recreate the former site in EJS syntax<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible"
content="IE=edge" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet"
href="main.css" />
<title>Info Site</title>
</head>
<body>
<h1>Info Site</h1>
<div>
there are <%=nouns.length%> nouns
</div>
</body>
</html>
the site should now look like this: note that our MVC structure makes it easy to swap templating engines:we simply swap the view component data is still sent as object to the view as before stylesheet is still used by the view in the same way as before convert the ES6-template-string syntax to EJS syntaxconvert this:<h2><b>API call: </b>There are ${nouns.length} nouns.</h2>
<div class="content">
${nouns
.filter((m, i) => i < 3)
.map((m) => m.article + ' ' + m.singular)
.join(', ')}, ...
</div>
to this:<h2><b>API call: </b>There are <%=nouns.length%> nouns.</h2>
<div class="content">
<ul>
<% for(const noun of nouns.slice(0,3)) { %>
<li><%=noun.article%> <%=noun.singular%></li>
<%}%>
<li>...</li>
</ul>
</div>
convert rest of site:<h2><b>Local JSON file: </b>There are <%=books.length%> books.</h2>
<div class="content">
<ul>
<% for(const
book
of
books.slice(0,3))
{
%>
<li>
<%=book.title%>
</li>
<%}%>
<li>...</li>
</ul>
</div>
<h2><b>Local Excel file: </b>There are <%=translations.length%> translations.</h2>
<div class="content">
<ul>
<% for(const
translation
of
translations.slice(0,3))
{
%>
<li>
<%=translation.fromPhrase%>
</li>
<%}%>
<li>...</li>
</ul>
</div>
<h2><b>Local directory of images: </b>There are <%=landscapePhotos.length%> landscape photos.</h2>
<div class="content">
<% for(const
fileName
of
landscapePhotos)
{
%>
<img src="images/<%=fileName%>" />
<%}%>
</div>
<h2><b>Local directory of markdown files: </b>There are <%=jobs.length%> jobs.</h2>
<div class="content">
<% for(const
job
of
jobs)
{
%>
<div class="job">
ID-CODE: <%=job.idCode%>
<%-job.html%>
</div>
<%}%>
</div>
note in particular this syntax:<img src="images/<%=fileName%>" />
<%-job.html%>
make nouns pagesserver.jsapp.get('/nouns', (req, res) => {
res.render('pages/nouns', siteData)
});
views/pages/nouns.ejs<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible"
content="IE=edge" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet"
href="main.css" />
<title>Info Site</title>
</head>
<body>
<h1>Info Site</h1>
<h2><b>API call: </b>There are <%=nouns.length%> nouns.</h2>
<div class="content">
<ul>
<% for(const
noun
of
nouns.slice(0,3))
{
%>
<li>
<%=noun.article%>
<%=noun.singular%>
</li>
<%}%>
<li>...</li>
</ul>
</div>
</body>
</html>
make header with nav bar with welcome and noun pageviews/partials/header.ejs<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible"
content="IE=edge" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet"
href="main.css" />
<title>Info Site</title>
</head>
<body>
<h1>Information Site</h1>
<nav>
<a href="/">welcome</a> |
<a href="nouns">nouns</a>
</nav>
views/partials/footer.ejs views/pages/nouns.ejs<%- include('../partials/header');%>
<h2><b>API call: </b>There are <%=nouns.length%> nouns.</h2>
<div class="content">
<ul>
<% for(const
noun
of
nouns.slice(0,3))
{
%>
<li>
<%=noun.article%>
<%=noun.singular%>
</li>
<%}%>
<li>...</li>
</ul>
</div>
<%- include('../partials/footer');%>
main.cssnav a {
text-decoration: none;
color: orange;
}
create rest of pages publish site to Heroku set up for Herokumake sure start runs node:"scripts": {
"dev": "nodemon server.js",
"start": "node server.js"
},
makes sure server uses dynamic portconst port = process.env.PORT || 3007;
log into: ../heroku.com https://heroku.com new app, give name, Europe [GitHub] [Connect to GitHub] search for repository, click [Connect] [Enable Automatic Deploys] [Overview] [Open app] to debug: heroku logs -a howto-node-express-ejs-mvc --tail