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
models/fruits.js
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 data
console.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
models/fruits.js
//at the bottom of /models/fruits.js
module.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 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 template 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
views/show.ejs
<!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
server.js
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:
views/show.ejs
<!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:
server.js
app.get("/fruits/", (req, res) => {
res.render("index.ejs", {
allFruits: fruits,
});
});
Create an index.ejs file
touch views/index.ejs
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:
views/show.ejs
<a href="/fruits">Back to Index</a>