Lesson Overview

So far in previous lessons we've started making basic MVC (Model View Controller) applications with Node.js and Express. We used JSON objects and also a JavaScript class as the model, we used some basic HTML and very basic EJS for our views, and we used a controller with all the behind-the-scenes processing. In this lesson we'll take a more detailed look at EJS and see what else you can do with it to create pages with more dynamic content.

Pre-Requisites

Before doing this lesson, make sure you've done the Express MVC lesson and that you've done the demonstration programs and exercises, as you'll continue building those in this lesson.

Intro to EJS Templates

Templates are views that contain placeholders. When a template page is going to be sent in a response, the placeholders are filled with actual data. In an Express application, this is done with the help of a template engine. A template engine looks for placeholders using its preferred syntax, and helps your code replace the placeholders with actual data.

Express supports a few different template engines. I'm only going to talk about the EJS (Embedded JavaScript) template engine, but you might be interested in learning about some of the others that are popular:

EJS is very simple and easy to learn: it's just JavaScript enclosed within <% and %> symbols. You just code your regular HTML, but place the code inside files with the .ejs extension and store them in the project's /views directory. You can then add any JavaScript code inside the special EJS tags, as described below.

EJS commands are enclosed inside special tags defined with the <% %> symbols as indicated below:

There are some other tags you can read about in the EJS documentation, but you won't be using them in this lesson.

Recall from the previous lesson that the EJS render engine needs to be set up in your app.js file by including the

app.set("view engine", "ejs");

statement. Also recall that the dependency will need to be added to a project as part of the npm install command.

Output Tags (Expressions)

As you saw in the Express MVC lesson, the EJS output tag <%= %> will simply output whatever value its expression evaluates to; the output tag is a basic placeholder, so it will be replaced with the expression's value. The expression can be a single variable (in which case the tag is replaced with the variable's value) or it can be any valid JavaScript expression (in which case the tag is replaced with the final result of the expression). For example, all of these are valid because each is an expression that yields a single value:

<%=dayInput%>
<%=(Math.PI * req.params.day ** 2).toFixed(2)%>
<%=(radius <= 0) ? radius : 'invalid, default of 1 used'))%>

Scriptlets: If Statements

Scriptlets allow you to include any JavaScript inside your .ejs files. For example, you can include any if statements or a switch statement inside your code.

Let's do a very basic example: set up a new project (I'm calling mine /ejs_ifs and it will have a base application path of "/esj1"). Add the /models, /views, and /controllers directories, plus the app.js file.

Add the usual code inside your app.js: strict mode, require express, create the express app, require the home controller, set the port # to 3000, set the view engine, and start the server.

Add a handler for GET requests to /ejs1 that include two parameters: one for user name and one for a rating number for the user's current mood from 1 to 3. These requests should be routed to a function in your main controller:

app.get("/ejs1/:user/:rating", homeController.renderIndex);

Add the rendering function to your controller to render the index.ejs page, and pass in the two URL parameter values:

exports.renderIndex = (req, res) => {
  res.render("index", {
      user: req.params.user,
      rating: req.params.rating
  });
}

Now add the index.ejs file to your /views directory and add the minimal HTML, plus a header and footer. Between the header and footer, add a <main> element.

Let's add a simple if statement: if the user name and rating are not empty/falsy, we'll display a level-2 heading with the user name. Otherwise we won't display anything:

<% if (user && rating) { %>
  

Welcome, <%=user%>!

<%}%>

Notice how this code was written? We put the start of the If-block inside a scriptlet tag - the scriptlet was closed right after the if's opening brace. Not only is this permitted, but encouraged! Scriptlets can't display output, so to display the heading, we have to close the scriptlet. We can finish the if-block (just it's closing brace is all that's needed) with the last scriptlet below the level-2 heading. When you have a lot of complex code, this can definitely make your code much harder to read and debug - that's why many beginners who start off with EJS eventually decide to learn an alternative such as PUG: PUG is not the same syntax as JavaScript, so it would be another new language to learn, but it's not difficult and it doesn't have all the annoying <% and %> everywhere, so it's easier to read.

Try your program out so far: don't forget to iniitalize your application with npm init and don't forget to install the express and ejs dependencies.

We can use the same kind of syntax to perform nested selections. For example, I'd like to add a switch statement that displays a text emoji (e.g. :) ) depending on the rating that was input via the URL parameter. I only want to do this if both the user name and rating are not empty. Be careful coding this because it's very easy to make mistakes!

<% if (user && rating) { 
  let output = ""; %>
  

Welcome, <%=user%>!

<% switch (parseInt(rating)) { case 1: output = ":)"; break; case 2: output = ":|"; break; case 3: output = ":("; break; default: output = "o.O"; }%>

<%=output%>

<%}%>

Note that because the values are coming in as strings, any URL parameters you want to calculate or compare with as numbers should be converted to integer (parseInt()) or float (parseFloat()).

This opens up many new possibilities for dynamic content: if you're familiar with the JavaScript selection structures, you can use them easily in your .ejs files! Of course, this also means you can use iteration structures, too.

Scriptlets: Loops

You'll probably use a lot of loops when creating dynamic content. For example, you might create a data table with some values that were read in from a JSON file or database. You might have a blog whose articles and comments are displayed iteratively based on cloud data.

Let's start off easily by creating some content with random numbers. The user can specify the minimum and maximum values, plus the number of values to generate, using URL parameters.

Set up a new project (I'll call this one /ejs_loops with a base application URL of /ejs2) and set it up with the same structure as the previous one.

In your app.js, add a GET request handler to /ejs2 that includes 3 parameters: miminum value, starting value, and number of values. The last parameter is optional (you should have learned how to do this in the exercises of the previous lesson). Add the function to the main controller that renders the index page and passes it the 3 parameter values. However, if the number of values parameter is empty or <= 0, give it a default value of 10. EJS templates don't play well with null values (not easily, anyway).

Let's add a counted for-loop to our .ejs file that prints the random numbers in a paragraph. Don't forget to parse each of the numeric values to an integer, otherwise your calculations will be off.

<p>
  <% min = parseInt(min);
  max = parseInt(max);
  num = parseInt(num);
  for (let i = 1; i <= num; i++) {
    let n = Math.floor(Math.random() * (max - min + 1) + min); %>
    <%=n%>
  <%}%>
</p>

Note once again that you have to close the scriptlet inside the for-loop (after the let n ... statement) so that you can output the value of n using an output tag. The scriptlet is re-opened at the end to close the for-loop brace.