MVC
#
Lesson Objectives- Define MVC and explain why it matters
- Move our data into a separate file
- Move our presentation code into an EJS file
#
Define MVC and explain why it matters- One of the core tenants of good programming is to compartmentalize your code
- Already our code is getting a little messy
- we have data, app instantiation (listening), and routes all in one file
- One way to organize the code is to separate it out into three sections:
- M odels
- data (javascript variables)
- V iews
- how the data is displayed to the user (HTML)
- C ontrollers
- the glue that connects the models with the views
- M odels
- This allows various developers to divide up a large code base
- minimizes likelihood of developers overwriting each others code
- allows developers to specialize
- one can focus just on getting good with dealing with data
- one can focus just on getting good with html
- one can focus just on getting good with connecting the two
- Think of MVC as a restaurant
- Models are the cook
- prepares food/data
- Views are the customer
- consumes food/data
- Controllers are the waiter
- brings food from cook to customer
- has no idea how food/data is prepared
- has no idea how the food/data is consumed
- Models are the cook
#
Move our data into a separate filemkdir models
touch models/fruits.js
- Put your fruits variable in there
fruits | -> models (folder) | -> fruits.js (file) | -> node_modules (folder) | -> package-lock.json (file) | -> package.json (file) | -> server.js (file)
const fruits = [ { name: "apple", color: "red", readyToEat: true, }, { name: "pear", color: "green", readyToEat: false, }, { name: "banana", color: "yellow", readyToEat: true, },];
- We now require that file in the original
server.js
const fruits = require("./models/fruits.js"); //NOTE: it must start with ./ if it's just a file, not an NPM package
// Test that we have our dataconsole.log(fruits);
Empty object! We could have multiple variables in our
/models/fruits.js
file.- How does javascript know which variable in
/models/fruits.js
to assign to the fruits const inserver.js
(the result of therequire()
statement)? - We must tell javascript which variable we want to be the result of the
require()
statement inserver.js
- How does javascript know which variable in
//at the bottom of /models/fruits.jsmodule.exports = fruits
Now you should see your fruits data console logged in terminal!
A common error - if you misspell module.exports
(ie module.export
) - you will get an empty object. Spelling matters!
#
Move our presentation code into an EJS fileNow we want to move our View code (HTML) into a separate file just like we did with the data
- Install the NPM package EJS (Embedded JavaScript)
npm install ejs
- this is a templating library that allows us to mix data into our html
- the HTML will change based on the data!
npm install ejs
- EJS Documentation
mkdir views
touch views/show.ejs
- this will be the html for our show route
- Put some html into
show.ejs
- html boilerplate
html tab
- Add title
- Add h1
<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title>Fruits Show</title> </head> <body> <h1>Fruits show page</h1> </body></html>
- Now, in
server.js
instead ofres.send(fruits[req.params.indexOfFruitsArray])
, we can callres.render('show.ejs')
- express will 'know' to look inside the
/views
directory - it will send the html in the
show.ejs
file as a response
#
Combining HTML and Data#
Part 1 - send the data to the fileNow lets mix our data into our HTML
- Our route is acting like the controller now (Remember model is the data, view is what the user sees - the controller handles the logic of getting the data and presenting it to the user). Let's pass the data to the view
res.render
is a function, it takes two arguments:
- the first is which file to render - always a string
- the second is which data should be sent to the file - always an object
app.get("/fruits/:indexOfFruitsArray", (req, res) => { res.render("show.ejs", { //second param must be an object // the key property is the name of the variable we will access the data in our file fruit: fruits[req.params.indexOfFruitsArray], //there will be a variable available inside the ejs file called fruit, its value is fruits[req.params.indexOfFruitsArray] });});
#
Part 2 Using & Rendering the Data in the file- In the second argument for our
res.render
function we named a keyfruit
- We can access our data in our file by using the same name
- We can also add JavaScript to our HTML, for example let's use an
if
statement:
<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title>Fruit Show</title> </head> <body> <h1>Fruits show page</h1> <p> The <%= fruit.name %> is <%= fruit.color %>. <% if (fruit.readyToEat === true) { %> <span> It is ready to eat </span> <% } else { %> <span> It is not ready to eat </span> <% } %> </p> </body></html>
- Note that there are two types of new tags
<% %>
run some javascript<%= %>
run some javascript and insert the result of the javascript into the HTML
#
Update Index RouteUpdate the index route in server.js
:
app.get("/fruits/", (req, res) => { res.render("index.ejs", { allFruits: fruits, });});
Create an index.ejs
file
touch views/index.ejs
<!DOCTYPE html><html lang="en" dir="ltr"> <head> <meta charset="utf-8" /> <title>Fruits Index</title> </head> <body> <h1>Fruits Index Page</h1> <ul> <% for (let i = 0; i < allFruits.length; i++) { %> <li> <a href="/fruits/<%=i%>"> <%= allFruits[i].name %> </a> </li> <% } %> </ul> </body></html>
Add a link back to the index route in show.ejs
:
<a href="/fruits">Back to Index</a>