Web Programming Header

CODE PAGE


EXPRESS

Express NodeJS App for Chinese Zodiac with year entry field on left and results page showing animal on the right


EXPRESS APP

We will make an app that asks for a year and shows a result page with the Chinese zodiac animal for that year. There will be a static index page and a dynamic template result page. Express will handle the routing of the data to serve the right pages. Thanks Wendi Jollymore for the prototype.

  1. Follow the NODE INFO instructions to make an app called zodiac
  2. Make sure you are in VS Code with only the zodiac directory
  3. Make sure you have an app.js (empty is fine for now)
  4. Make sure you do the npm init

  5. Install Express. In the terminal type:

  6. // Install Express:
    npm i express
    

  7. Note the packages added to node_packages
  8. Note the change to the package.json
  9. We will also use a package to get the Chinese Animal of the Year
  10. And we will install nodemon for automatic refreshing

  11. // Install Chinese-Year:
    npm i chinese-year
    
    // Install nodemon:
    npm i -D nodemon
    
    // in the package.json add the following to the scripts:
    // "start": "nodemon app.js" 
    // remember to add a comma on the line before this


    TEMPLATING

  12. We will use a Template Engine for our results page
  13. This will take data and insert it into our HTML
  14. There are different Engines - we will use EJS (Embedded JavaScript)

  15. // Install EJS:
    npm i ejs
    


    INDEX AND CSS PAGES

  16. Pay close attention to the folders we use as they change a few times!
  17. Make a folder called public in your zodiac folder
  18. Add an index.html to the public folder with the following code:

  19. <!doctype html>
    <html>
    
    <head>
        <meta charset="utf-8">
        <title>Chinese Zodiac Animal</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="zodiac.css">
    </head>
    
    <body>
        <header>
            <img src="assets/zodiac.jpg" alt="Chinese Zodiac Animals">
            <h1>Chinese Zodiac Animal</h1>
        </header>
    
        <form action="getyear">
            <p>
                <label for="year">Enter Year:
                    <input type="number" max="4000" value="2024" name="year" id="year">
                </label>
            </p>
            <input type="hidden" name="special" value="test special & normal characters!">
            <p><input type="submit" value="Get Zodiac Animal"></p>
        </form>
    
        <footer>
            <img src="assets/zodiac.jpg" alt="Chinese Zodiac Animals">
        </footer>
    
    </body>
    
    </html>  
    

  20. Add an assets folder to your public folder
  21. Get the zodiac image from here ZODIAC JPG
  22. And put the image in your asset folder
  23. Add a zodiac.css page to your public folder with this code:

  24. body {
        text-align: center;
        margin:20px;
        background-color:#b90b0c;
        font-family:'Lucida Sans';            
    }
    header {
        color:#eee;
        margin-bottom:30px
    }
    header img, footer img {
        margin:20px 0px 10px 0px;
        width:60%;
        min-width:400px;
        max-width:600px;
    }
    footer img {
        margin-top:40px
    }
    form {
        font-size:24px;
        background-color:rgb(255, 114, 114);
        display:inline-block;
        width:300px;  
        padding:20px 30px;
        border:thick solid #ddd;
        border-radius:30px;
    }
    input {
        font-size:24px
    }
    input[type=submit] {
        padding:20px 40px;
        background-color: rgb(207, 0, 0);
        color:white;
        cursor:pointer;
        border-radius:10px;
        border:none;
    }
    input[type=submit]:hover {
        background-color: red;
    }


    RESULT PAGE

  25. Make a views folder in your zodiac folder NOT in the public folder
  26. Add a result.ejs page to the views folder with this code:
  27. Note the extension is ejs and not html and it is not in the public folder
  28. This is an html page where data will be inserted in the <%=variable %>

  29. <!doctype html>
    <html>
    
    <head>
        <meta charset="utf-8">
        <title>Results for Chinese Zodiac Animal</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="zodiac.css">
    </head>
    
    <body>
        <header>
            <img src="assets/zodiac.jpg" alt="Chinese Zodiac Animals">
            <br><br><br>
            <h1><%= year %> is the year of the <%= animal %></h1>
        </header>   
    
        <footer>
            <img src="assets/zodiac.jpg" alt="Chinese Zodiac Animals">
        </footer>
    
    </body>
    
    </html>
    


    EJS TAGS

  30. Here are the types of EJS tags for reference
  31. There is no need to put them anywhere...

  32. // EJS tags:
    
    <%  %>   // holds JavaScript but will not output
    <%= %>   // outputs the value of content within 
    <%- %>   // outputs the content but escapes HTML charcters (use to output HTML)
    <%# %>   // is a comment tag or just add a # to other tags to ignore them    
    
    // the JavaScript tags can go around HTML and will connect up
    // which is nice for conditionals (seen below) and loops
    
    <% 
    const message = "oh well"; // or perhaps from form data...
    const rand = Math.random();
    if (rand > 0.8) { %>
        <h2>wow</h2>
    <% } else { %>
        <h2><%= message %></h2>
    <% } %>
    


    APP PAGE

  33. Add the following code to the app.js page in the zodiac folder

  34. "use strict";
    
    const express = require("express");
    const app = express();
    
    app.set("port", process.env.PORT || 3000);
    
    // set where static files such as html, images, css pages are located
    app.use(express.static('public'));
    
    // set how we will render to template files
    app.set("view engine", "ejs");
    
    // bring in the chinese-year functionality
    const chineseYear = require("chinese-year");
    
    
    // ROUTING 
    // Routing is choosing what to do for various urls 
    // In Express you can app.use(function) which uses the function to do something 
    // This can also be used to call (mount) middleware (from other packages)
    // server requests can be routed with get(), post() - and others like all()
    // which are special versions of use() that capture get or post data
    // Route callbacks receive request (in), response (out) and next parameters
    // We can make our own callbacks 
    // and if we do, they are responsible for calling next()
    // otherwise the flow will stop
    
    app.use((req, res, next)=>{ 
        console.log(req.method + " request made to: " + req.url);
        // console.log(req.headers);
        next();
    });
    
    // PATH
    // Here we pass a path of "/test" to use() - regular expressions are allowed here too
    // This means, run this code if the URL starts with /test 
    // note: app.get("/test") only runs if the url is exactly /test or with ? data after it
    // To test, run the app and in a browser use http://localhost:3000/test
    // this code will end the routing
    app.use("/test", (req, res, next)=>{ 
        console.log("starting with test");
        res.send("<h1>TEST</h1>");
        res.end(); // do not go any further
    });
    
    // GET AND POST
    // Below we check if we receive get or post data using routes that come with Express
    // we do this for /getyear which comes from the action of the form
    // (test by setting the form action=post to run the post code down below)
    // we could route other urls to and through different functions
    
    // ~~~~~~~~~~~~~~~~~
    // GET
    
    app.get("/getyear", (req, res)=>{
    
        // get data can be collected with req.query
        // this is an object with properties that match the names of the form fields
        console.log(req.query);
    
        const year = req.query.year ? req.query.year : new Date().getFullYear();
    
        // get the animal from chinese-animal 
        const animal = chineseYear.getAnimal(year);
    
        // this would send some html to the client
        // but we want to load the template page instead
        // // res.send("<h1>"+animal+"</h1>");
    
        // just need result and not result.ejs
        // and pass it the data which is made available 
        // to JavaScript on the template page
        res.render("result", {year:year, animal:animal}); // or just {year, animal} in ES6
    
    });
    
    // ~~~~~~~~~~~~~~~~~
    // POST
    
    // When using POST - the data is in the body
    // Express's urlencoded method returns a function for the app to use
    // that will receive request, response and next parameters
    // then parse the data in the request, and add properties 
    // that match the field names to the request body object
    // and then it calls next()
    app.use(express.urlencoded({extended:false}));
    
    // if collecting post data then this route will run
    app.post("/getyear", (req, res)=>{
    
        // note we collect the year in the body - not the query
        console.log(req.body);
    
        const year = req.body.year ? req.body.year : new Date().getFullYear();
        const animal = chineseYear.getAnimal(year);
    
        // use res.render() to render an ejs file
        res.render("result", {year:year, animal:animal}); // or  {year, animal} in ES6
    
    });
    
    
    app.listen(app.get("port"), ()=>{
        console.log("running on " + app.get("port"));
    });
    


    RUNNING THE APP

  35. In the terminal (CTRL `) or pull up from bottom of VS Code
  36. Run the following code:

  37. // type the following and press Enter
    node app
    
    // or if nodemon is installed and in the "start" : "nodemon app.js" 
    // is in the scripts of the package.json then type
    npm start
    


  38. Open the app in a browser at http://localhost:3000
  39. Choose a year and submit the form
  40. Note the comments in the terminal

FULL Sheridan Node documentation can be found in Wendi Jollymore's Lessons.