Begin with the End In Mind

New, Create, Delete, Update, Edit Routes

  1. Create a New, Create, Delete, Update, Edit routes and appropriate views
  2. Add interactivity to your site with forms
  3. Create a put & delete requests
  4. Define middleware
  5. View body of a post request
  6. Redirect the user to another page

Explanation

Remember where we are going

arthur_node_jsx_diagram_photoshopped

Where Does It Fit in With MVC

URL HTTP Verb Action Used For Mongoose Method View
/fruits/ GET index Displaying a list of all fruits Fruit.find Index.jsx
/fruits/new GET new Display HTML form for creating a new fruit none New.jsx
/fruits/:id DELETE destroy Delete a specific photo Fruit.findByIdAndRemove or Fruit.findByIdAndDelete none
/fruits/:id PATCH/PUT update Update a specific fruit Fruit.findByIdAndUpdate or Fruit.findOneAndUpdate none
/fruits POST create Create a new fruit Fruit.create none
/fruits/:id/edit GET edit Return an HTML form for editing a fruit Fruit.findOne or Fruit.findById Edit.jsx
/fruits/:id GET show Display a specific fruit Fruit.findOne or Fruit.findById Show.jsx

Note: Only GET Requests render a View In RESTful Routing

Understanding Structure

Connections Schema Models Document Collection Database Cluster
In server.js, or connection file Connect to DB using Connection URL In model file identify the structure of your data Use the schema to create a new Model that can connect to DB with JavaScript methods like .find, create and .findOneAndUpdate An instance of the Model that passes the schema, residing in a collection The group of documents that were added to the database by a specific Model A collection of all your collections, every app should have it's own Database All your databases in your Atlas Account live in your cluster, you only need one cluster and all your databases go in the cluster

For people that know SQL

  1. Collection ====> Table
  2. Document ====> Row
  3. Key on Schema =====> Column

Pick Up Where we left off in Index.jsx

<nav>
    <a href="/fruits/new">Create a New Fruit</a>
</nav>

Have Create Route redirect to Index After Fruit Creation

Inside the create route

Fruit.create(req.body, (error, createdFruit)=>{
    res.redirect('/fruits');//sends to /fruits get route
});

Update Show.jsx

  1. Edit the views/fruits/Show.jsx file
  2. Change JSX
const React = require('react');

class Show extends React.Component {
    render(){
      const { name, color, readyToEat } = this.props.fruit
        return (
            <div>
                <h1>{ name } show page</h1>
                The { name } is { color }
        { readyToEat ? `It is ready to eat` : `It is not ready to eat` }
            </div>
        )
    }
}
module.exports = Show;

Render the jsx

app.get('/fruits/:id', (req, res)=>{
    Fruit.findById(req.params.id, (err, foundFruit) => {
        res.render('fruits/Show', {
            fruit:foundFruit
        });
    });
});

Test that you can do the following

  1. Go to http://localhost:3000/fruits/new
  2. Create a new fruit
  3. See the fruit at http://localhost:3000/fruits
  4. Show page for fruit works
  5. You can see fruit in your MongoDB Atlas DB fruits database

Review Questions

❓ In your own words, describe the use case for Mongoose (what is it's purpose and when might you choose to use it?).

❓ A Mongoose _ is used to make a Mongoose Model.

❓ We use a Mongoose _ to perform CRUD operations on a MongoDB..

require('dotenv').config()
const express = require('express')
const mongoose = require('mongoose')
const fruits = require('./models/fruits')

const app = express()

app.use(express.urlencoded({ extended: true}))
app.set('view engine', 'jsx');
app.engine('jsx', require('jsx-view-engine').createEngine());
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });
mongoose.connection.once('open', ()=> {
    console.log('connected to mongo');
});

// Index
app.get('/fruits', (req, res)=>{
    Fruit.find({}, (error, allFruits)=>{
        res.render('fruits/Index', {
            fruits: allFruits
        });
    });
});

// New
app.get('/fruits/new', (req, res) => {
    res.render('fruits/New');
});

// Delete

// Update

// Create
app.post('/fruits', (req, res)=>{
    if(req.body.readyToEat === 'on'){ //if checked, req.body.readyToEat is set to 'on'
        req.body.readyToEat = true;
    } else { //if not checked, req.body.readyToEat is undefined
        req.body.readyToEat = false;
    }
    Fruit.create(req.body, (error, createdFruit)=>{
      res.redirect('/fruits');//sends to /fruits get route which renders index page
    })
  })

// Edit

// Show
app.get('/fruits/:id', (req, res)=>{
    Fruit.findById(req.params.id, (err, foundFruit)=>{
        res.render('fruits/Show', {
            fruit:foundFruit
        });
    });
});

app.listen(PORT, () => {
  console.log(`${PORT} is lit`)
})

❓ Essential Review Questions

  1. What is the difference between New and Create?
  2. What is Middleware?

Deleting & Updating Data

Big Gotcha

  • We already used a form to create a fruit but now we want to be able to update or delete the data we added
  • First instinct is to use another form which is correct, but we should also consider the fact that PUT & DELETE requests cannot be sent through HTML Forms by default
  • HTML Forms can only send GET & POST Requests without the aide of JavaScript

We can use middleware to help us

  • We will use a middleware package called method-override
  • Middleware is just a fancy way to say that you want to take some code and run it in the middle of when the user sends a request and when the express passes the request to the relevant route handler
  • User sends request
  • Middleware function runs
  • Then express passes it the relevant routeHandler that was created with app.get, or app.post etc
  • Method Override will run a function that checks to see if you received a post request that actually supposed to be an PUT or DELETE etc.
npm i method-override

Now, in our server.js file, add:

//include the method-override package place this where you instructor places it
const methodOverride = require('method-override');
//...
//after app has been defined
//use methodOverride.  We'll be adding a query parameter to our delete form named _method
app.use(methodOverride('_method'));

Add Routes Delete, Edit & Update Finishing Induces

Delete

app.delete('/fruits/:id', (req, res)=>{
    Fruit.findByIdAndRemove(req.params.id, (err, data)=>{
        res.redirect('/fruits');//redirect back to fruits index
    });
});

Edit

app.get('/fruits/:id/edit', (req, res)=>{
    Fruit.findById(req.params.id, (err, foundFruit)=>{ //find the fruit
      if(!err){
        res.render(
    		  'fruits/Edit',
    		{
    			fruit: foundFruit //pass in the found fruit so we can prefill the form
    		}
    	);
    } else {
      res.send({ msg: err.message })
    }
    });
});

Update

app.put('/fruits/:id', (req, res)=>{
    if(req.body.readyToEat === 'on'){
        req.body.readyToEat = true;
    } else {
        req.body.readyToEat = false;
    }
    Fruit.findByIdAndUpdate(req.params.id, req.body, (err, updatedFruit)=>{
       console.log(updatedFruit)
        res.redirect(`/fruits/${req.params.id}`);
    });
});

Edit View

Now the lets make the JSX file by using the command line to touch a Edit.jsx file in our views folder, then opening it in VS Code:

const React = require('react');

class Edit extends React.Component{
  render() {
    const { name, _id, readyToEat color } = this.props.fruit
    return (
      <>
      <h1>{name} Edit Page</h1>
      <form action={`/fruits/${_id}?_method=PUT`} method="POST">
          Name: <input type="text" name="name" defaultValue={name}/><br/>
          Color: <input type="text" name="color"  defaultValue={color}/><br/>
          Is Ready To Eat:
              { readyToEat? <input type="checkbox" name="readyToEat" defaultChecked />: <input type="checkbox" name="readyToEat"/> }
          <br/>
          <input type="submit" value="Submit Changes"/>
      </form>
      </>
    )
  }
}
module.exports= Edit;

Add Delete Button to Index and Show Page By adding the following code

  <form action={`/fruits/${fruit._id}?_method=DELETE`} method="POST">
    <input type="submit" value="DELETE"/>
  </form>

In Index.js

{
  fruits.map((fruit,i) => {
            return (<li key={fruit._id}>
                        <a href={`/fruits/${fruit._id}`}>
                        {name}</a> is {color}

                         <br/>
                        {
                            readyToEat?
                            'It\'s ready to eat':
                            'It\'s not ready to eat'
                        }
                        {/* Your Delete Form Goes Here  It's incomplete we will fix below*/}
                      <form action={`/fruits/${fruit._id}?_method=DELETE`} method="POST">
                        <input type="submit" value="DELETE"/>
                      </form>
                    </li>
                  )
        })
}