Taking it One Step Further
by Alex Merced
In this lesson we will learn:
- how to use EJS Partials
- Using the Bulma CSS Framework
- Making an Ajax call with Axis
Setup
- generate a new project with the following command
npx degit AlexMercedCoder/ejs-express-supreme#main marvelheroes - cd into the new folder
- run
npm install - rename the template.env file to .env and make sure to add your MongoDB URI to the proper env variable
- run the dev server and go to localhost:3000 to make sure all is working
Exploring
This template has several things already to go for us, feel free to use it in future projects.
- Sessions configured (just needs the MONGODB_URL & SECRET env variables)
- Mongo connection configured (just needs MONGODB_URL env variable)
- EJS Partials Pre-set
- Bulma already linked in the EJS head tag
- Axios already installed for Ajax calls
EJS Partials
EJS partials allow us to take repeated parts of our website like our head tag, header/navigation and footer and define them in separate file we can then inject in multiple views.
You'll notice a views/home.ejs file that already exists... views/home.ejs is being rendered by our home route in our home router, it's our main page
<!DOCTYPE html>
<html lang="en">
<!-- THE HEAD TAG PARTIAL -->
<%- include("partials/head") %>
<body>
<!-- THE HEADER TAG PARTIAL -->
<%- include("partials/header") %>
<main>
</main>
<!-- THE HEAD TAG PARTIAL -->
<%- include("partials/footer") %>
</body>
</html>The include function injects the contents of the referenced EJS file in that location of the template. If we need to make a change to the header or footer of all our pages we can just edit one partial instead of many files containing all the code.
Conventionally, all partials are put in a folder called views/partials to distinguish them from your main views.
The Head Partial
views/partials/head.ejs
<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" />
<title>My Website</title>
<!-- BULMA CSS - https://bulma.io/ -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css"
/>
<link rel="stylesheet" href="/css/style.css" />
</head>Here is our head partial, we can use it to link our own CSS and JS files stored in the public folder and we can use to pull in third party css and js libraries. Above we can see we have import the Bulma CSS framework (a popular CSS framework along with Bootstrap and Materialize).
The Header Partial
<header>The Header</header>Not much is going on in the header partial other than a no frill header tag. We can use this to create a navigation bar and site logo. Let's use Bulma to create an attractive navbar. The way CSS frameworks work is by having several pre-made classes we can inject on our elements to get great visuals.
The best way to start with a CSS framework is with their documentation where you can find snippets of pre-made components you can then alter to fit your desired element. The below is a modified from the Navbar section of the Bulma documentation.
<header>
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="https://bulma.io">
<img src="https://upload.wikimedia.org/wikipedia/commons/b/b9/Marvel_Logo.svg" width="112" height="28">
</a>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item">
Home
</a>
<a class="navbar-item">
Documentation
</a>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
More
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
About
</a>
<a class="navbar-item">
Jobs
</a>
<a class="navbar-item">
Contact
</a>
<hr class="navbar-divider">
<a class="navbar-item">
Report an issue
</a>
</div>
</div>
</div>
</div>
</nav>
</header>The Footer Partial
Let's adapt the footer component from the Bulma docs for our footer partial!
<footer class="footer">
<div class="content has-text-centered">
<p>
<strong>MarvelApp</strong> by You!. The source code is licensed
</p>
</div>
</footer>Getting Marvel Data from an API
Currently our route for our main page looks like this...
routes/home.js
///////////////////////////////
// Router Routes
////////////////////////////////
router.get("/", (req, res) => {
res.render("home")
})Along from getting data from our database, we can also get data from 3rd party APIs. A wonderful thing about calling third party APIs from the backend is NO CORS!!! Since CORS is a browser feature it doesn't apply when a server makes a request directly to another server.
Although, since we aren't using jQuery we don't have access to the $.Ajax function. Usually you have two main choices for backend Ajax requests.
- fetch: While fetch is built into the browser, to use fetch in node you have to install
node-fetch - axios: another popular http request library, this is already installed in this boilerplate. (check the package.json to be sure, if not
npm install axios).
Import Axios Into our HomeRouter
We will import some marvel hero data from this url:
https://dummydata.netlify.app/marvel.jsonSo let's update our routes/home.js file like so!
NOTE: When Axios returns the result of the API call, the actual data received in the request is stored in a data property of the response object. This is why we see (response.data) below. response is the object returned by Axios and inside the object the data from our request is stored in the data property, so the result is response.data to access to requested data
///////////////////////////////
// Import Router
////////////////////////////////
const router = require("express").Router()
const axios = require("axios")
///////////////////////////////
// Router Specific Middleware
////////////////////////////////
///////////////////////////////
// Router Routes
////////////////////////////////
router.get("/", async (req, res) => {
// make api call
const response = await axios("https://dummydata.netlify.app/marvel.json")
// get specifically the hero data from the results
const heroes = response.data.children[0].children
// send hero data to our rendered view
res.render("home", {
heroes
})
})
///////////////////////////////
// Export Router
////////////////////////////////
module.exports = routerRendering Our Heroes
Now we can update our home.ejs to render our hero data, thanks to our partials there isn't much clutter. What we'll do is use a Bulma containter component and Bulma cards to render each of the heroes.
views/home.js
<!DOCTYPE html>
<html lang="en">
<!-- THE HEAD TAG PARTIAL -->
<%- include("partials/head") %>
<body>
<!-- THE HEADER TAG PARTIAL -->
<%- include("partials/header") %>
<main>
<!-- //////////////////////////////////////////////// -->
<div class="container" style="display: flex; flex-wrap: wrap; justify-content: space-around;">
<% for (hero of heroes) { %>
<div class="card" style="margin: 30px;">
<div class="media-content">
<p class="title is-4"><%=hero.hero%></p>
<p class="subtitle is-6"><%=hero.name%></p>
</div>
<div class="content">
<a href="<%=hero.link%>" ><button class="button">BIO</button></a>
</div>
</div>
<% } %>
</div>
<!-- //////////////////////////////////////////////// -->
</main>
<!-- THE HEAD TAG PARTIAL -->
<%- include("partials/footer") %>
</body>
</html>Now on to Lab!