Intro to 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 keep an app from getting messy is to separate it out into three sections
- Models
- data (javascript variables)
- Views
- how the data is displayed to the user (HTML)
- Controllers
- the glue that connects the models with the views
- Models
- 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
-
Create a directory called
models
inside our app directory -
Inside
/models
, create a data file named fruits.js -
Put your fruits variable in there
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
-
But, 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()
statment)? - We must tell javascript which variable we want to be the result of therequire()
statement inserver.js
//at the bottom of /models/fruits.js
module.exports = fruits
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)
- 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
-
Create a views directory inside our app directory
-
Inside
/views
, create a file called show.ejs- this will be the html for our show route
-
Put some html into show.ejs
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Fruits App</title> </head> <body> <h1>Fruits show page</h1> </body> </html>
-
Now, instead of
res.send('some text')
, 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
app.get("/fruits/:indexOfFruitsArray", (req, res) => { res.render("show.ejs") })
-
Now lets mix our data into our HTML
-
Our route is acting like the controller now. Let's gather the data and pass it to the view
app.get("/fruits/:indexOfFruitsArray", (req, res) => { res.render("show.ejs", { //second param must be an object fruit: fruits[req.params.indexOfFruitsArray], //there will be a variable available inside the ejs file called fruit, its value is fruits[req.params.indexOfFruitsArray] }) })
-
Access the data in the view:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Fruits App</title> </head> <body> <h1>Fruits show page</h1> The <%=fruit.name; %> is <%=fruit.color; %>. <% if(fruit.readyToEat === true){ %> It is ready to eat <% } else { %> It is not ready to eat <% } %> </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/", (request, response) => {
response.render("index.ejs", {
allFruits: fruits,
})
})
Create an index.ejs
file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fruits App</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>