The Web Developer Bootcamp 2024

Colt Steele

Back to Templating Index Page


 

Express EJS Project
Intro

We've built an express ejs project which uses several technologies to demonstrate several principles, including how to process Js code within an html file (using Embedded Js (ejs)), and also, how to render applications from the server-side.

One page displays a random number generator, and another simple list output. Very simple stuff here, but invaluable information in learning how to process Js within an html file, and also how to generate server-side rendered output.

And lastly, we created a "sub-Reddit" project meant to demonstrate these basic technologies and their use. This is not a fully functional Reddit clone. We are simply going to be demonstrating how to pull data from an external source and create a template to display the data.

To view these files in our browser, we need our server running, so:

  1. Open your Terminal application
  2. Navigate to your Project Directory (where your index.js file is located)
  3. Type node index.js or nodemon index.js
Your server should now be running and because we added a "console.log()" statement to our index.js file, it will display "Server Online : Listening on port: 3000", in the Terminal. And now with our server running, and in the browser address bar, go to http://localhost:3000/home

To build the subReddit part of this project, we are going to be pulling data from a .json file. This file contains data for three different sub-reddits. The relative .json file can be viewed here., and the technologies that we're going to be utilizing are:

  1. HTML
  2. JavaScript
  3. node.js
  4. nodemon
  5. Express
  6. Embedded JavaScript (ejs)
We will be combining our HTML, and Js code into ejs files.

Basic Setup

For our Basic Setup, we will have:

  1. Our "Parent" directory is set up. (called express)
  2. Our package.json file is set up.
  3. Express is installed (node_modules directory)
  4. EJS is installed (node_modules directory)
  5. nodemon is installed (for build purposes)
  6. Our index.js Server File is set up
  7. We have our "Static Files Directory" and it's sub-directories:
    • assets
      • css
      • files
      • js
  8. we have our views folder for our four ejs files:
    • views
      • home.ejs
      • pets.ejs
      • subReddit.ejs
      • notFound.ejs
  9. and we have our includes files a header/footer/navbar"
    • includes
      • footer.ejs
      • header.ejs
      • navbar.ejs

Directory Structure

Here is our complete directory structure:

  • Folder Icon  express
    • Folder Icon  assets
      • - Folder Icon  css
        • ejs Icon  bootstrap.min.css
        • ejs Icon  localStyles.css
        • ejs Icon  root.css
      • - Folder Icon  files
        • - Folder Icon  json
          • ejs Icon  ejsDemoData.json
      • - Folder Icon  js
        • ejs Icon  bootstrap.min.js
        • ejs Icon  jquery-3/7/1/min.js
    • Folder Icon  node_modules
    • Folder Icon  views
      • // experimental view to render Bodingles Home on server
      • // this view is entirely separate from our subReddit EJS Project
      • - Folder Icon  bodingles
        • - Folder Icon  includes
          • ejs Icon  footer.ejs
          • ejs Icon  header.ejs
        • ejs Icon  bodIndex.ejs
      • // subReddit view to render on server
      • - Folder Icon  subReddit
        • - Folder Icon  includes
          • ejs Icon  footer.ejs
          • ejs Icon  header.ejs
          • ejs Icon  navbar.ejs
        • ejs Icon  home.ejs
        • ejs Icon  pets.ejs
        • ejs Icon  subReddit.ejs
        • ejs Icon  notFound.ejs
    • ejs Icon  index.js
    • ejs Icon  package-lock.json
    • ejs Icon  package.json

Server Code

Here is our complete Server Setup:

  • JavaScript
  • index.js | Server Code
  •  
  • // Set the variables and requires
  • const express = require( 'express' );
  • const app = express();
  • const port = 3000;
  • const path = require( 'path' );
  • const demoData = require( './assets/files/json/ejsDemoData.json' );
  •  
  • // set our view engine to ejs
  • app.set( 'view engine', 'ejs' );
  •  
  • // define our "path" to the views sub-directories
  • // we create an array because there are two separate views (Bodingles & subReddit)
  • // when you make a req in the browser, it searches ALL of the views listed in the array for a match
  • app.set( 'views' [
    • path.join(__dirname, 'views/bodingles'),
    • path.join(__dirname, 'views/subReddit')
  • ])
  • // define our "path" to the "static files" directory
  • app.use(express.static(path.join(__dirname, 'assets')))
  •  
  • // get request for root view (Bodingles) : experiment in server rendered Bodingles Home
  • app.get( '/', ( req, res ) => {
    • res.render('bodIndex')   
  • })
  •  
  • // get request for root view (express ejs app)
  • app.get( '/home', ( req, res ) => {
    • const num = Math.floor(Math.random() * 100) + 1;
    • res.render('home', { num })
  • })
  •  
  • // get request for pets view
  • app.get( '/pets', ( req, res ) => {
    • const pets = [ 'Chico', 'Bella', 'Sadie', 'Addie Cakes', 'BooBoo', 'Jasper', 'Jitters' ];
    • res.render('pets', { pets })
  • })
  •  
  • // get request for subreddit view
  • app.get( '/r/:subreddit', ( req, res ) => {
    • // params breaks down the req object and returns an array with the object parameters
    • const { subreddit } = req.params;
    • const data = demoData[subreddit];
    • if ( data ) {
      • // the 3 dots allow us to "spread" the data to get to the individual pieces, such as "Name"
      • res.render(' subreddit', { ...data })
    • } else {
      • // if requested page is not found
      • res.render( 'notFound', { subreddit })
  • })
  •  
  • // Start Server : Listening on {port}
  • app.listen( port, () => {
    • console.log(`Server Online : Listening on port: ${ port }`);
  • })

We can notice a couple of important issues here:

  • - we are requiring our .json file, which will give us access to it's data.
  • - if we look at our get request for our subReddits, we see that we're using the params method
    • - this will return the individual parameters of the req object, and assign it to the variable as an object
  • - next we notice that we are assigning out .json file data, as an array.
    • - this is because we have three subReddits within the .json file and the will be allocated to the variable as three array parameters.
  • - next is an error check to make sure the sub-Reddit the user is searching for, actually exists
    • - if it does: we'll break the sub-Reddit data into usable pieces using ... and render it for output.
    • - if not: we'll render the notFound page.

A brief discussion about using an array to define our view paths. I was experimenting with running a Bodingles page that was rendered on the server. But since I already had my subReddit ejs demo, I ended up with two entirely different views that are not related. But, how to be able to access each one, and their corresponding sub-pages, with one index.js file?

Turns out the array is the answer. When we are defining our views path's, if we list multiple views within an array, the server will search through all of the views listed in the array to find a match. If it doesn't find one, it's going to render the notFound.ejs page.

the Includes

We are going to be using includes in our process, so we can demonstrate how to use them. Remember the includes will go in the views/includes sub-directory.

Now in our original Bodingles html app, which renders on the client-side, we used external, header/footer Js files that are using innerHTML to build and display the header and footer. In the individual html pages that use the header/footer, we simply add an empty <div> with an id that we access in the Js file. And of course we need the <script> elements before our closing <body> elements So in any of our html pages, we'll have:

  • html
  • // any html file
  • <body>
    • <div id="header"></div>
    • ...
    • <div id="footer"></div>
    •  
    • <script src="header.js"></script>
    • <script src="footer.js"></script>
  • </body>

Now in our header/footer Js files, we'd have:

  • JavaScript
  • header.js
  • const head = document.querySelector('#header');
  • head.innerHTML = 'All header HTML code goes here';
  • or
  • footer.js
  • const foot = document.querySelector('#footer');
  • foot.innerHTML = 'All footer HTML code goes here';

This works but remember, all of these files are rendered on the client-side.

We want to incorporate the same general process of having external files that we can incorporate into other files, but we want to do this while rendering everything, on the server-side. As it turns out, we can utilize essentially the same process with includes, but done a little differently.

Remember that server-side rendering can be done with html files, but with limitations. These file can't access any external links such as css or Js.

These files are normally written with html coding, but saved as ejs files. This is done so that we can "embed" Js coding within our html coding, which is how our includes works. They are Js coding written within an html page.

One last note about our includes files here. We do NOT need to create a complete HTML page with all the essential elements such a <head> and <body> elements. We ONLY need the HTML coding for the specific data that we want in our include.

So, for example, the code for our header/footer.ejs include files will look something like this:

  • HTML/EJS
  • header.ejs
  • <header>
    • header html coding goes here
  • </header>
  • or
  • footer.ejs
  • <footer>
    • Footer html coding goes here
  • </footer>

Remember, we save these files as ejs files (header.ejs/footer.ejs).

And finally, we can "include" our includes files, into our other ejs files. Remember, they MUST be ejs files so that we can embed the Js coding within our html coding. The include call is one line of Js code, written within some html coding, using the ejs syntax, like so:

  • HTML/EJS
  • // any ejs file that wants to include these external ejs files
  • <body>
    • <%- include('includes/header') %>
    • ...
    • ... other page coding here ...
    • ...
    • <%- include('includes/footer') %>
  • </body>

Very simple coding here to include our external header/footer.ejs files. A couple of things to note here:

  1. Any external "includes"" files can be either html or ejs files, however
    • if there is any "scripting" or js coding involved, they must be ejs files
  2. Any pages that want to include the external "include" files must be ejs files.
  3. And finally, we must use <%- as the ejs syntax to generate the include.

So we should be able to create our include files, save them in the includes sub-directory, and actually include them into any ejs file. We can now set up our individual pages (views).

Setting Up our Views

We need to understand that the point of the subReddit ejs project is to be able to set up a server that will pull data from an external source and produce an output. The "pulling" of the data, it's processing, and rendering, are all going to be handled on the server side.

Also, we are going to be rendering our output (our views), as ejs files, so we need to point out a couple of distinctions here, between ejs files, and html files. Both file types output their data from html coding. However, there are a couple of important differences:

  • - html files are rendered on the client side which means they can utilize external files such as css, and Js files.
  • - ejs files however, are rendered on the server side. This means that they can NOT access external css or Js files. But they can process Js internally within the html file itself, and we'll use this capability to render our output.

So we start by creating a basic html file, by opening a new empty file and (in vsCode) pressing [sh + !]. This will give us the basic html structure we need for an ejs file.

  • *note: you could of course set this up manually, and type in all the required html info, but it is much simpler to let vsCode do the work for us.
And now we have an empty html file with the basic structure. We can set a title if we want. And we need to save this file, but NOT as an html file, but as an ejs file. So we'll make sure we're in our "views" directory, and save our file as [filename.ejs].

We're going to have four files (views) in our complete express ejs project, so we create and save the following files:

  1. home.ejs // displays a random number generator plus a couple of "lorem inpsum" paragraphs
  2. pets.ejs // displays a bulleted list
  3. subReddit.ejs // displays three different sub-reddits: chickens, harvest, and soccer
  4. notFound.ejs // displays message if query not found

And now our basic files are set up and ready to go. We need to understand that, all we are concerned about for the rendering, is what code is contained, within the body element of the page. This is where we'll be writing our code for our output. No "css" files. No "script" files. Just html, and embedded Js (ejs) coding with the proper "ejs syntax".

So for our breakdown of our code, we are not going to list the entire file contents, only the coding that resides within the <body> - </body> elements of the page. Also, I'm not going to do a breakdown of the "Bodingles" project. That was a just an experiment to see if I could make it work. But I did mention it earlier in the folder setup, and in the server code, but only to show how we could incorporate multiple views, within one index.js file.

So here are our views:


home.ejs
  • EJS
  • home.ejs
  • <body>
    •   <%- include('includes/header') %>  
    • <div>
      • // Bootstrap NavBar
      •   <%- include('includes/navBar') %>  
      •  
      • <h3>This is my EJS   <%= 'demo '.toUpperCase() %>   Page</h3>
      • <p>Todays Random Number is: <b><%= num %></b> and it's an
      •           <b>  <%= num%2===0 ? 'EVEN' : 'ODD' %>   </b>number.</p>
      •  
      • <p>lorem ipsum ...</p>
      • <p>lorem ipsum ...</p>
    • </div>
    •   <%- include('includes/footer') %>  
  • </body>

Notice that we are including our header/footer external files, and also our Bootstrap NavBar we generated as a separate include file in our includes sub-directory. And in other sections, we have some pure Js coding, all embedded within our html structure.

Also, notice the use of <%-, and <%= for the Js coding. This is the "proper syntax" for ejs files:

  • <%= %> "Outputs the value into the template (HTML escaped)", which means, it processed the Js code and converted it into html output.
  • <%- %> "Outputs the unescaped value into the template", which means, it runs the embedded Js code to produce an output, but doesn't convert anything.
This is exactly why we're using ejs files as opposed to pure html files. Remember, ejs is "embedded" JavaScript. It allows us to embed Js coding within html code.


pets.ejs
  • EJS
  • pets.ejs
  • <body>
    •   <%- include('includes/header') %>  
    • <div>
      • // Bootstrap NavBar
      •   <%- include('includes/navBar') %>  
      • <h1>express Demo</h1>
      •  
      • <h5>These are Our Pets</h5>
      • <hr>
      •   <% for ( pet of pets ) { %>  
        • <p>  <%= pet %>  </p>
      •   <% } %>  
    • </div>
    •   <%- include('includes/footer') %>  
  • </body>

Notice here that we've introduced <%, that is a 'Scriptlet' tag, for control-flow. It runs the Js code, similar to <%-, but it produces no output. So you would use this syntax anytime you just want to run some embedded Js code, such as our for loop.


subReddit.ejs
  • EJS
  • subReddit.ejs
  • <body>
    •   <%- include('includes/header') %>  
    • <div>
      • // Bootstrap NavBar
      •   <%- include('includes/navBar') %>  
      •  
      • <h1>subReddits Demo</h1>
      • // all variable data is received from the index.js app.GET
      • // all subReddit data originates from ejsDemoData.json
      • <h2>  <%= name %>  </h2>
      • <h4>  <%= description %>  </h4>
      • // subscriber info
      • <p>Total Subscribers: <b>  <%= subscribers %>  </b></p><hr>
      •  
      • // loop to display all posts
      •   <% for ( post of posts ) { %>  
        • <article>
          • // post author and title
          • <p><b>  <%= post.author %>  </b>:   <%= post.title %>  </p>
          • // if post has an image check
          •   <% if (post.img) { %>  
            • <img src="  <%= post.img %>  " alt="  <%= post.title %>  ">
          •   <% } else { %>  
            • <p><b>No Image Available</b></p>
          •   <% } %>  
        • </article><hr>
      •   <% } %>  
    • </div>
    •   <%- include('includes/footer') %>  
  • </body>

And now we are using a variety of ejs Js to produce our output. Remember that, what we have created here is a template. This template will produce the same layout for all of our sub-Reddit pages (views), but each individual page (view), will have it's own set of data.


notFound.ejs
  • EJS
  • notFound.ejs
  • <body>
    •   <%- include('includes/header') %>  
    • <div>
      • // Bootstrap NavBar
      •   <%- include('includes/navBar') %>  
      • <h6>We're sorry, but the   <%= subreddit %>   subreddit was not found.</h6>
    • </div>
    •   <%- include('includes/footer') %>  
  • </body>

The reason for this file is that the end user could type a subReddit they might be looking for directly into the browser address bar. But if the subReddit doesn't exist, then we will get an error and our app will crash. To prevent this, we create a page that simply says, "We're sorry but that sub-reddit can not be found. So our app continues to run.


Running the App

Now we can finally discuss how to actually run the app. Since we are using server side processing, we are going to need to be running our "localhost" server. Simply open the "Terminal" of your choice, navigate to our apps root directory, (that we named express), and type either:

  • node index.js, or
  • nodemon index.js

The server should now be running, so we then open our browser window, and because we defined our port number to be 3000 (in index.js), we type (in the address bar):

which brings up our root app which is our Bodingles experiment. To display our other views, type: And as for our subReddits, we set these up in our index.js to use a /r/ designation to identify them as sub-reddit pages, so: will bring up our sub-reddit pages.

And there we go, our Express ejs Demo app is up and running. We retrieved our data from an external source (ejsDemoData.json), and utilized server-side processing to process the data, and output it for viewing, (using the ejs file format).


Back to Top