Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

first draft #8

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "todo-api",
"version": "1.0.0",
"description": "**Objective:** Use Express to make a RESTful API for a to do list. Build a client for your app that uses AJAX and Handlebars templating to `CREATE`, `READ`, `UPDATE`, and `DELETE` todos.",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.14.1",
"express": "^4.13.3",
"hbs": "^4.0.0"
},
"devDependencies": {},
"repository": {
"type": "git",
"url": "git+https://github.com/stevennoble78/express-todo-app.git"
},
"bugs": {
"url": "https://github.com/stevennoble78/express-todo-app/issues"
},
"homepage": "https://github.com/stevennoble78/express-todo-app#readme"
}
73 changes: 73 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- set viewport to device width to make site responsive -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- bootstrap css -->
<link type="text/css" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<!-- custom styles -->
<link rel="stylesheet" type="text/css" href="main.css">
<title>To Do List</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1 class="text-center">To Do List</h1>
<br>
<form id="newTodo">
<div class="form-group">
<input type="text" class="form-control form-group" name="task" placeholder="Task">
<input type="text" class="form-control form-group" name="description" placeholder="Description">
<input type="submit" class="btn btn-warning btn-block">
</div>
</form>
<div id="todo-list">
<div class="row">
<div class="col-md-4">
<h2>Task</h3>
</div>
<div class="col-md-8">
<h2>Description</h4>
</div>
</div>
<script type="text/x-handlebars-template" id="template">
{{#each todos}}
<div class="row">
<div class="col-md-4">
<h3>{{task}}</h3>
</div>
<div class="col-md-8">
<h4>{{description}}</h4>
<div class="btn-group">
<span class="glyphicon glyphicon-pencil btn btn-warning" id="{{_id}}" aria-hidden="true"></span>
<span class="glyphicon glyphicon-trash btn btn-warning" id="{{_id}}" aria-hidden="true"></span>
<span class='glyphicon glyphicon-'
</div>
<form class="editTodo" id="form{{_id}}">
<div class="form-group">
<input type="text" class="form-control form-group" name="task" value="{{task}}">
<input type="text" class="form-control form-group" name="description" value="{{description}}">
<input type="submit" class="btn btn-warning btn-block">
</div>
</form>
</div>
</div>
{{/each}}
</script>
</div>
</div>
</div>
</div>

<!-- jquery -->
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<!-- bootstrap js -->
<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<!-- handlebars -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.3/handlebars.min.js"></script>
<!-- custom script -->
<script type="text/javascript" src="main.js"></script>
</body>
</html>
3 changes: 3 additions & 0 deletions public/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.editTodo {
display: none;
}
118 changes: 118 additions & 0 deletions public/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// wait for DOM to load before running JS
$(document).ready (function () {

// check to make sure JS is loaded
console.log('JS is loaded!');

// Compile the template
var source = $('#template').html();
var template = Handlebars.compile(source);
var todoResults = [];
var baseUrl = '/';
var apiUrl = baseUrl + 'api/todos/';
var $todolist = $('#todo-list');
var $newTodo = $('#newTodo');

// Handlebars list helper
Handlebars.registerHelper('list', function(context, options) {
var ret = "<ul>";
for(var i=0, j=context.length; i<j; i++) {
ret = ret + "<li>" + options.fn(context[i]) + "</li>";
}
return ret + "</ul>";
});

// Use AJAX to get data and append it to the page
$.get(apiUrl, function(data) {
console.log(data);
todoResults = data;

// Render the data
var todoHTML = template({todos: todoResults});
$todolist.append(todoHTML);
});

// Refresh function
function refresh (data) {
console.log('refreshing');
$todolist.empty();
$('input').val('');
// Rerender the data
var todoHTML = template({todos: todoResults});
$todolist.append(todoHTML);
}

// Add todo function called by submit button handler
function addTodo(data) {
todoResults.push(data);
refresh();
}

// Put todo function called by glyphicon pencil handler
function putTodo() {
event.preventDefault();
var id = $(this).attr('id');
$('#form' + id).toggle();
$('#form' + id).on('submit', function(event) {
event.preventDefault();
var updatedTodo = $(this).serialize();
$.ajax({
type: 'PUT',
url: apiUrl + id,
data: updatedTodo,
success: function (data) {
// Get the object to update
var todoToUpdate = todos.filter(function(todo) {
return (todo._id === id);
})[0];
// Remove the object and replace it with the updated object
todoResults.splice(todoResults.indexOf(todoToUpdate), 1, data);
// var index;
// for (var i=0; i<todoResults.length; i++) {
// if (todoResults[id] === id) {
// index = i;
// }
// }
// todoResults.splice(index, 1);
// todoResults.push(data);
refresh();
}
});
});
}

// Delete todo function called by glyphicon trash handler
function deleteTodo() {
event.preventDefault();
var id = $(this).attr('id');
$.ajax({
type: 'DELETE',
url: apiUrl + id,
success: function (data) {
var index;
for (var i=0; i<todoResults.length; i++) {
if (todoResults[id] === id) {
index = i;
}
}
todoResults.splice(index, 1);
refresh();
console.log('deleted');
}
});
}

// Click handler for Submit button to add a todo
$newTodo.on('submit', function(event) {
event.preventDefault();
var newTodo = $(this).serialize();
$.post(apiUrl, newTodo, addTodo);
});

// Click handler for glyphicon pencil
$todolist.on('click', '.glyphicon-pencil', putTodo);

// Click handler for glyphicon trash
$todolist.on('click', '.glyphicon-trash', deleteTodo);

});
108 changes: 108 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SERVER SIDE JAVASCRIPT

var express = require('express');
var bodyParser = require('body-parser');
var hbs = require('hbs');

var app = express();

// Set up static public file
app.use(express.static('public'));

// Set up body-parser
app.use(bodyParser.urlencoded({ extended: true }));

// Seed data
var todos = [{
_id: 1,
task: 'wake up',
description: 'get up at 7:00am',
done: false
},{
_id: 2,
task: 'make breakfast',
description: 'cook some eggs and toast',
done: false
},{
_id: 3,
task: 'eat breakfast',
description: 'eat it',
done: false
},{
_id: 4,
task: 'clean up',
description: 'wash dishes',
done: false
}];

// Set up route for index.html
app.get('/', function(req, res) {
res.render('index');
});

// Set up route for get
app.get('/api/todos', function(req, res) {
res.json(todos);
});

// Set up route for getting a single todo
app.get('/api/todos/:id', function(req, res) {
// Get the id
var todoId = parseInt(req.params.id);
// Find the object by the id
var todoToDisplay = todos.filter(function(todo) {
return (todo._id === todoId);
});
// Respond with JSON data
res.json(todoToDisplay);
});

// Set up route to post todos
app.post('/api/todos', function(req, res) {
// Create a new todo with form data
var newTodo = req.body;
// Set a sequential id for todo data
if (todos.length > 0) {
newTodo._id = todos[todos.length - 1]._id + 1;
} else {
newTodo._id = 1;
}
// Add new todo to the todos array
todos.push(newTodo);
// Send new todo as JSON resonse
res.json(newTodo);
});

// Set up route for updates
app.put('/api/todos/:id', function(req, res) {
// Get todo id from url params
var todoId = parseInt(req.params.id);
// Find todo to update by its id
var todoToUpdate = todos.filter(function(todo) {
return (todo._id === todoId);
})[0];
// Update the todo's task
req.body._id = todoId;
todos.splice(todos.indexOf(todoToUpdate), 1, req.body);
// We could update the task and the description as well
// Send back JSON data
res.json(req.body);
});

// Set up route for deletes
app.delete('/api/todos/:id', function(req, res) {
// Get todo id from url params
var todoId = parseInt(req.params.id);
// Find todo to update by its id
var todoToDelete = todos.filter(function(todo) {
return (todo._id === todoId);
})[0];
// Delete the todo's task
todos.splice(todos.indexOf(todoToDelete), 1);
// Send back JSON data
res.json(todoToDelete);
});

var server = app.listen(process.env.PORT || 5000, function() {
console.log('listening...');
});