FSJS Week 6 - You had me at update/You delete me.
Outline
- Set up for week6
- Create a form to edit files
- Create the Update endpoint and connect it to the front-end
- Create the Delete endpoint and connect it to the front-end
1. Setup Project
-
Get the latest version of the group project
git clone https://github.com/CodeLouisville/FSJS-class-project.git
or if you already have a clone repository
git pull
-
Remove
week6
(because we are going to recreate it today) and copyweek5
toweek6
rm -rf week6 cp -r week5 week6 cd week6
-
Install packages
npm install
-
Start application
node index.js
Create a form
Give our Angular app the ability to select an active file
-
Edit
public/js/template/file-list.template.html
so that each repeated<li>
element has a click-handlers. The directive to use isng-click="$ctrl.selectFile(file)"
. The<li>
element should look like:<li ng-repeat="file in $ctrl.files" ng-click="$ctrl.selectFile(file)"> <h2>{{file.title}}</h2> <p>{{file.filename}}</p> </li>
ng-click: https://docs.angularjs.org/api/ng/directive/ngClick
-
Add a small function in our component (
public/js/component/file-list.component.js
) to output the passed argument:this.selectFile = (file) => { console.log("I GOT A FILE!!!!", file); }
-
Reload the page, open a console window (
cmd-opt-I
orctrl-shift-I
), and click on a few files to test; -
Alter the function to unselect any currently selected file, and select the current file.
this.selectFile = (file) => { // get the currently selected file and set the `selected` // property to false const currentlySelected = this.selectedFile; this.selectedFile = null; // Mark the passed file as selected if (!currentlySelected || currentlySelected._id !== file._id) { this.selectedFile = angular.copy(file); } }
-
When a file is selected, we want its list item to change appearance. We'll do that by adding a class to the
<li>
element with the following directive:ng-class="{selected: file === $ctrl.selectedFile}"
<li ng-repeat="file in $ctrl.files" ng-click="$ctrl.selectFile(file)" ng-class="{selected: file._id === $ctrl.selectedFile._id}"> <h2>{{file.title}}</h2> <p>{{file.filename}}</p> </li>
And update our css file
public/css/style.css
:li { cursor: pointer; } li.selected { background-color: pink }
ng-class: https://docs.angularjs.org/api/ng/directive/ngClass
Create a form that shows the selected file
-
Create a section that only shows up when a file is selected.
<!-- public/js/template/file-list.template.html --> <div ng-show="$ctrl.selectedFile"> <h4>Editing: {{$ctrl.selectedFile.title}}</h4> </div>
-
Reload and click around.
-
Add a form:
<div ng-show="$ctrl.selectedFile"> <h4>Editing: {{$ctrl.selectedFile.title}}</h4> <fieldset> <div> <label>Title: <input ng-model="$ctrl.selectedFile.title" /></label> </div> <div> <label>Filename: <input ng-model="$ctrl.selectedFile.filename" /></label> </div> <div> <button ng-click="$ctrl.updateFile($ctrl.selectedFile)">Update File</button> <button ng-click="$ctrl.deleteFile($ctrl.selectedFile)">Delete</button> </div> </fieldset> </div>
ng-model: https://docs.angularjs.org/api/ng/directive/ngModel
-
Create a stub of a 'submit' form in our component:
this.updateFile = (file) => { console.log("I am PUT-ing", file); } this.deleteFile = (file) => { console.log("I am DELETE-ing", file); }
-
Reload and test
Create the update endpoints
Create the controller
-
Create a directory under routes:
src/routes/controllers
:mkdir src/routes/controllers
-
Create a file for our controllers
touch src/routes/controllers/file.controller.js
-
Copy our existing
GET
controller fromsrc/routes/index.js
and export it fromfile.controller.js
const mongoose = require('mongoose'); module.exports = { // List all files in the database list: function(req, res, next) { mongoose.model('File').find(function(err, files) { if (err) { console.log(err); res.status(500).json(err); } res.json(files); }); }, }
The Module Object: https://nodejs.org/api/modules.html#modules_the_module_object
-
Import our controller and use that in the route:
const fileController = require('./controllers/file.controller'); //... router.get('/files', fileController.list);
-
Restart the server and test that you can still list files
-
Create a route in
/src/routes/index.js
that handlesPUT
requests, and use an currently-not-existing controller:router.put('/files/:fileId', fileController.update);
-
Now create the
update
controller in/src/routes/controllers/file.controller.js
module.exports = { // ... // Update a file update: function(req, res, next) { const fileId = req.params.fileId; const updatedFile = req.body.file; mongoose.model('File').findById(fileId, function(err, file) { if (err) { console.log(err); res.status(500).json(err); } file.title = updatedFile.title; file.filename = updatedFile.filename; file.save(function(err, file) { if (err) { console.log(err); res.status(500).json(err); } res.json(file); }); }); }, }
Model.findById(): http://mongoosejs.com/docs/api.html#model_Model.findById
Hook up the front-end to the back end
-
First, move the bit of code which was responsible for getting the list of files in to its own function, so that it can be called whenever needed. Also, make sure to call that function right after creation.
this.getFiles = () => { return $http.get("/files").then(function(response){ return self.files=response.data; }); } this.getFiles();
-
In
public/js/component/file-list.component.js
, replace theupdateFile
stub with the following code:this.updateFile = (file) => { $http.put(`/files/${file._id}`, {file}) .then(response => { console.log("Successfully updated file"); return this.getFiles(); }) .catch(err => { console.log("Oops...there was an error", err); }) }
$http.put: https://docs.angularjs.org/api/ng/service/$http#put
-
Restart server, reload page and test
Create the delete endpoints
Create the controller
-
Go back to
src/routes/index
and add the following route:router.delete('/files/:fileId', fileController.delete);
-
Create the controller in
/src/routes/controllers/file.controller.js
{ // ... // Delete a file delete: function(req, res, next) { const fileId = req.params.fileId; mongoose.model('File').findById(fileId, function(err, file) { if (err) { console.log(err); res.status(500).json(err); } file.remove(function(err, file) { if (err) { console.log(err); res.status(500).json(err); } res.json(file); }) }) } }
Document.remove(): http://mongoosejs.com/docs/api.html#model_Model-remove
-
In
public/js/component/file-list.component.js
, replace thedeleteFile
stub with the following code:this.deleteFile = (file) => { $http.delete(`/files/${file._id}`) .then(response => { console.log("Successfully deleted file"); this.selectedFile = null; return this.getFiles(); }) .catch(err => { console.log("Drat...there was an error", err); }) }