People List EJS: Code-Along
The current People List app works for a small number of people, but it becomes increasingly tedious to add more and more. It is frustrating, because so much of the HTML is repeated, and all of the data exists in people.json. If only there were a way to pull the data from the JSON file into the HTML, so the server could dynamically generate each page and update the homepage accordingly... well, there is!
EJS (Embedded JavaScript) allows developers to use JavaScript directly in HTML templates, making them much more dynamic. The server passes a JavaScript object into the HTML based on the specific request and current data. It then renders a specific HTML page to send back up to the client for display!
Setting Up
- In the People List project folder, create a new folder named "views"
- Move the home.html file and person0.html file into the "views" folder
- Rename the files to home.ejs and person.ejs
app.js Updates
Make the following updates to the app.js file.
- Remove the
pathmodule import as it is no longer necessary - At the top of the file, add
require('ejs')to install the library - After the
appvariable is initialized, use the code below to set the "view engine" to "ejs"app.set('view engine', 'ejs'); - In the body of the
homePagefunction, remove the current command and replace it withresponse.render('home'); - In the
personPagefunction, replace the render withresponse.render('person') Remove everything from the
personPagefunction except for the part where theindexquery parameter is obtainedfunction personPage(request, response) { let parsedUrl = url.parse(request.url, true); let index = Number(parsedUrl.query.index); response.render('person'); }- Run the server, and make sure the homepage still loads along with the first person page (currently ignoring the index)
Passing the JSON Person
The goal is to have the program dynamically generate an HTML page for each person in the people.json file. To do that, it is necessary to pull the correct person object from the people.json array based on the index, and pass it into the template.
Note: The people.json file must be saved in the same directory as the app.js file
- At the top of the file, import the
fsmodule, storing it in aconstvariableconst fs = require('fs'); - Under that, use
fs.readFileSyncandJSON.parseto put thepeople.jsonarray into a variable namedpeopleJsonlet rawData = fs.readFileSync('people.json'); let peopleJson = JSON.parse(rawData); - In the body of the
personPagefunction, use theindexquery parameter to get the correct person from thepeopleJsonarraylet currentPerson = peopleJson[index]; - In the
response.renderfunction call, pass in a second parameter that is a new object with apersonproperty ofcurrentPersonresponse.render('person', { person: currentPerson }); - In the person.ejs file, add
<%= JSON.stringify(person) %>somewhere for testing purposes - Run the server, load the
/personpage, and make sure that the object displayed changes based on theindexquery parameter!
Updating the Person Page Template
Now that the person object is available in the person.ejs template, all that's left is updating the template to use the object properties! A value from the person object can be implanted in the HTML like so:
<%= person.PROPERTY %>
- Replace the existing name with a dynamic piece of code that gets the
first_namefrom theperson, and adds thelast_nameof theperson- Use
<%=to start the EJS segment - Use
%>to end the EJS segment - All the JavaScript in between those tags will be rendered in the HTML file!
- Use
- Replace the existing occupation with another dynamic EJS segment using
person.occupation - Replace the image source (between
'') with another dynamic EJS segment usingperson.avatar - Check the
/personpage with some differentindexvalues to see it update dynamically!
Changing the Background Color
The background color should be green if the person is alive, and red if the person is dead. It is possible to use if statements in EJS scriptlets to accomplish this.
- Remove everything from the
headelement, and add an EJS scriptlet using<%and%>- For control flow, use
<%with no equals sign because no JavaScript should render to the template - Instead of rendering within EJS, these statements use HTML dynamically
- For control flow, use
- Within the EJS scriptlet, enter the first line of an
ifstatement checkingperson.alive<% if (person.alive) { %> - Under the EJS scriptlet, in the "body" of the
ifstatement, add astyleelement with the CSS to set the background color of the page tomediumseagreen<style> body { background: mediumseagreen; } </style> - Under the HTML, add another EJS scriptlet to close off the
ifstatement:<% } %> - Load up the
/personpage, and make sure only alive people have green backgrounds! - In the
ifclosing EJS scriptlet, add anelseto handle dead people<% } else { %> - Under the EJS scriptlet, in the "body" of the else, add a
styleelement with the CSS to set the background color of the page tored - Under the
</style>, add another EJS scriptlet to close off theelsestatement - Load up the
/personpage, and make sure that dead people have red backgrounds!
Code
<html>
<head>
<% if (person.alive) { %>
<style>
body {
background: mediumseagreen;
}
</style>
<% } else { %>
<style>
body {
background: red;
}
</style>
<% } %>
</head>
<body>
<h3>Person Information</h3>
<p>Name: <%= person.first_name + ' ' + person.last_name %></p>
<p>Occupation: <%= person.job %></p>
<img src='<%= person.avatar %>' />
<p><a href='/'>Home</a></p>
</body>
</html>
Updating the Home Page Template
Now that the person page dynamically loads each individual person, update the home page so that the list of links is generated dynamically too!
Setting up the Loop
- In the app.js file, update the
homePagefunction so that it passespeopleJsonwhen rendering the pageresponse.render('home', { people: peopleJson }); - In the home.ejs file, remove any
lielements- They will be dynamically generated!
Within the
ul, add an EJS scriptlet containing aforloop that will loop through all of the people in thepeoplearray<% for (let i = 0; i < people.length; i++) { %> <% } %>- In the "body" of the
forloop, add anli - In the
li, add an EJS segment to render the current index in theforloop:<%= i %> - Load up the homepage, and make sure the proper number of list items appears!
Creating the Person Links
- Around the
iEJS segment, add anaelement that will link to the person with the giveniindex<li> <a href='/person?index=<%= i %>'>Person</a> </li> - Update the text of the Person link so that it also provides the index
- Update the text of the Person link so that it contains the first name of the person at the given index
- Update the text of the Person link so that it contains both the first and the last name of the person
<a href='/person?index=<%= i %>'> <%= people[i].first_name + ' ' + people[i].last_name %> </a> - Load up the homepage, and make sure the links function properly!
Code
<html>
<body>
<h1>People List</h1>
<p>Welcome to the list of people</p>
<ul>
<% for (let i = 0; i < people.length; i++) { %>
<li>
<a href='/person?index=<%= i %>'>
<%= people[i].first_name + ' ' + people[i].last_name %>
</a>
</li>
<% } %>
</ul>
</body>
</html>
Final app.js Code
const express = require('express');
const url = require('url');
const fs = require('fs');
require('ejs');
const hostname = '0.0.0.0';
const port = 8080;
let rawData = fs.readFileSync('people.json');
let peopleJson = JSON.parse(rawData);
let app = express();
app.set('view engine', 'ejs');
function homePage(request, response) {
response.render('home', {
people: peopleJson
});
}
function personPage(request, response) {
let parsedUrl = url.parse(request.url, true);
let index = Number(parsedUrl.query.index);
let currentPerson = peopleJson[index];
response.render('person', {
person: currentPerson
});
}
app.get('/', homePage);
app.get('/person', personPage);
function listenCallback() {
console.log('Server running');
}
app.listen(port, hostname, listenCallback);