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 file#
mkdir modelstouch 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.jsfile.- How does javascript know which variable in
/models/fruits.jsto 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 = fruitsNow 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 file#
Now 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 viewstouch 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.jsinstead ofres.send(fruits[req.params.indexOfFruitsArray]), we can callres.render('show.ejs')
- express will 'know' to look inside the
/viewsdirectory - it will send the html in the
show.ejsfile as a response
Combining HTML and Data#
Part 1 - send the data to the file#
Now 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.renderis 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.renderfunction 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
ifstatement:
<!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 Route#
Update 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>