TodoList with AngularJS(Frontend), ExpressJS(Backend) and MySQL

Hetzel Cordoba
6 min readApr 11, 2016

Today we’re going to develop TodoList web app, for the frontend let’s use AngularJS, for the backend we’re going to use ExpressJS to manage all the rest requests, and finally for MySQL for the database.

We can use the Yo distribution to generate angularjs-gulp projects
https://github.com/Swiip/generator-gulp-angular, I personally use gulp because of the browser-sync option

mkdir frontend && cd $_
yo gulp-angular
gulp serve

The directory distribution created for the project is:

-bower_components
-dist
-e2e
-gulp
-node_modules
-src
-app
-assets

In the /src/app/main/main.html file erase all the content and paste the code below:

<div class=”container”><div class=”text-center”>
<h1>Todo List </h1>
<div class=”row”>
<div class=”btn-group pull-left” role=”group” aria-label=”…”>
<button type=”button” class=”btn btn-success” ng-click=”addTask()”>Add task</button>
</div>
<div class=”btn-group pull-right” role=”group” aria-label=”…”>
<button type=”button” class=”btn btn-default” ng-click=”order(‘name’)”>Name</button>
<button type=”button” class=”btn btn-default” ng-click=”order(‘dueDate’)”>Due Date</button>
<button type=”button” class=”btn btn-default” ng-click=”order(‘priority’)”>Priority</button>
</div>
</div>
</div>
<div class=”row”>
<div class=”col-md-6" >
<h2>Pending </h1>
<ul class=”list-group” ng-repeat=”task in tasks | orderBy:predicate:reverse | filter: earlyDueDateTask(task)”>
<li class=”list-group-item”>{{::task.name}}</li>
</ul>
</div>
<div class=”col-md-6" >
<h2>Overdue </h1>
<ul class=”list-group” ng-repeat=”task in tasks | orderBy:predicate:reverse | filter: lateDueDateTask(task)”>
<li class=”list-group-item text-danger”>{{::task.name}}</li>
</ul>
</div>
</div>
</div>

The result should look something like this:

The code above is the principal file, when the web app starts, this is the first page that will show. Here we’re going to show all the todo-list items.

In the controller file /scr/app/main/main.controller.js we’re going to add the methods to get the task date using momentjs, the filter methods to sort the list and go to the new task page.

(function() {
‘use strict’;
angular
.module(‘frontend’)
.controller(‘MainController’, MainController);
/** @ngInject */
function MainController($timeout, webDevTec, toastr, $scope, $resource, $apiUrl, moment, $state) {
$scope.arto=0;$scope.actualDate = moment();$scope.getAllTasks = function(){
var allTasksResource = $resource( $apiUrl + ‘/task’, {userId:’@id’}, {‘get’: {method:’GET’, isArray:true}});
allTasksResource.get()
.$promise.then(function(tasks) {
$scope.tasks = tasks;
console.log($scope.tasks);
});
};
$scope.getAllTasks();$scope.reverse = true;
$scope.order = function(predicate) {
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
$scope.predicate = predicate;
};
$scope.lateDueDateTask = function() {
return function( task ) {
var taskDate = moment (task.dueDate);
return taskDate >= $scope.actualDate;
};
};
$scope.earlyDueDateTask = function() {
return function( task ) {
var taskDate = moment (task.dueDate);
return taskDate <= $scope.actualDate;
};
};
$scope.addTask = function (){
$state.go(‘new-task’);
};
}
})();

Don't worry for the $resource call just yet, when we create the backend this will work.

In the /src/app folder let’s create a new folder called newTask, create the newTask.html and newTask.controller.js to handle the creation of new tasks.

For the newTask.html let’s add a form with three fields (name, date and priority),.

<div class=”container”><div class=”text-center”>
<h1>Todo List </h1>
</div>
<div class=”row”>
<h2>New Task </h2>
<form ng-submit=”addTask()”>
<div class=”form-group”>
<label >Task name</label>
<input type=”text” class=”form-control” ng-model=”task.name” placeholder=”I have to dev the test” required>
</div>
<div class=”form-group”>
<label >Due date</label>
<input type=”date” class=”form-control” ng-model=”task.dueDate” required>
</div>
<div class=”form-group”>
<label >Priority</label>
<input type=”number” class=”form-control” ng-model=”task.priority” required max=”5" min=”1">
</div>
<button type=”submit” class=”btn btn-default”>Add</button>
</form>
</div>
</div>

The result of this html should look like this:

In the newTask.controller.js we can create the method to handle the creation of a new task.

(function() {
‘use strict’;
angular
.module(‘frontend’)
.controller(‘NewTaskController’, NewTaskController);
/** @ngInject */
function NewTaskController($timeout, webDevTec, toastr, $scope, $resource, $apiUrl, moment, $state) {
console.log(‘new taskk’);
$scope.task = {};
$scope.addTask = function (){
console.log($scope.task);
var addTaskResource = $resource( $apiUrl + ‘/task/create’);
addTaskResource.save($scope.task)
.$promise.then(function(user) {
$state.go(‘home’);
});
};
}
})();

Now for the backend we need Mysql and ExpressJS, below you can find the terminal commands to install it if you don't have it already.

sudo apt-get install mysql-server
npm install express-generator -g

To generate a project in ExpressJS let’s

express backend

In the app.js let’s add the /task api, and the sequelize option to connect to MySql.

var express = require(‘express’);
var path = require(‘path’);
var favicon = require(‘serve-favicon’);
var logger = require(‘morgan’);
var cookieParser = require(‘cookie-parser’);
var bodyParser = require(‘body-parser’);
var cors = require(‘cors’);
var routes = require(‘./routes/index’);
var tasks = require(‘./routes/tasks’);
var app = express();// view engine setup
app.set(‘views’, path.join(__dirname, ‘views’));
app.set(‘view engine’, ‘jade’);
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, ‘public’, ‘favicon.ico’)));
app.use(logger(‘dev’));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, ‘public’)));
app.disable(‘etag’);
app.use(cors());
app.use(‘/’, routes);
app.use(‘/task’, tasks);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error(‘Not Found’);
err.status = 404;
next(err);
});
// error handlers// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render(‘error’, {
message: err.message,
error: {}
});
});
var Sequelize = require(‘sequelize’)
, sequelize = new Sequelize(‘tasks’, ‘root’, ‘hetzel’, {
dialect: “mysql”, // or ‘sqlite’, ‘postgres’, ‘mariadb’
port: 3306, // or 5432 (for postgres)
});
sequelize
.authenticate()
.then(function(err) {
console.log(‘Connection has been established successfully.’);
}, function (err) {
console.log(‘Unable to connect to the database:’, err);
});
sequelize
.sync({ force: true })
.then(function(err) {
console.log(‘It worked!’);
}, function (err) {
console.log(‘An error occurred while creating the table:’, err);
});
module.exports = app;

Let’s create the models, first create a new folder called models and create two new files, one called index.js and the other called task.js

The /models/index.js is going to handle the automatic migrations on the models:

/**
* Created by het on 4/1/16.
*/
“use strict”;var fs = require(“fs”);
var path = require(“path”);
var Sequelize = require(“sequelize”);
var env = process.env.NODE_ENV || “development”;
var config = require(path.join(__dirname, ‘..’, ‘config’, ‘config.json’))[env];
var sequelize = new Sequelize(config.database, config.username, config.password, config);
var db = {};
fs
.readdirSync(__dirname)
.filter(function(file) {
return (file.indexOf(“.”) !== 0) && (file !== “index.js”);
})
.forEach(function(file) {
var model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(function(modelName) {
if (“associate” in db[modelName]) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;

The task.js is going to have three fields (name, dueDate and prority).

/**
* Created by het on 4/1/16.
*/
“use strict”;
module.exports = function(sequelize, DataTypes) {
var Task = sequelize.define(“Task”, {
name: { type: DataTypes.STRING, allowNull: false },
dueDate: { type: DataTypes.DATE, allowNull: false },
priority: { type: DataTypes.INTEGER, allowNull: false, validate: { isIn: {
args: [[1, 2, 3, 4, 5]],
msg: “Validation error: \”24\” Rule \”in(1,2,3,4,5)\” failed.”
}
} }
});return Task;
};

Now create a config folder with a config.js file in it, this file will manage the database login for the different environments.

{
“development”: {
“username”: “your-user”,
“password”: “your-pass”,
“database”: “tasks”,
“host”: “127.0.0.1”,
“dialect”: “mysql”
},
“test”: {
“username”: “your-user”,
“password”: “your-pass”,
“database”: “tasks_test”,
“host”: “127.0.0.1”,
“dialect”: “mysql”
},
“production”: {
“username”: “your-user”,
“password”: “your-pass”,
“database”: “tasks_production”,
“host”: “127.0.0.1”,
“dialect”: “mysql”
}
}

Finally, in the routes folder we create a file called task.js to manage all the api request calls, the post create, the get task by id, the update and delete task.

var models = require(‘../models’);
var express = require(‘express’);
var router = express.Router();
/* GET users listing. */
router.get(‘/’, function(req, res, next) {
models.Task.findAll()
.then(function(tasks) {
console.log(tasks);
res.setHeader(‘Content-Type’, ‘application/json’);
res.json(tasks);
}).catch(function(error) {
console.log(error);
res.send(error.errors);
});
});router.post(‘/create’, function(req, res) {models.Task.create({
name: req.body.name,
dueDate: req.body.dueDate,
priority: req.body.priority
}).then(function(task) {
console.log(‘created’);
res.setHeader(‘Content-Type’, ‘application/json’);
res.json(task);
}).catch(function(error) {
console.log(error);
res.send(error.errors,500);
});
});router.get(‘/destroy/:id_task’, function(req, res) {models.Task.find({ where: { id: req.params.id_task } })
.then(function(task) {
if (task!=null){
models.Task.destroy({
where: {
id: req.params.id_task
}
}).then(function(task_resp) {
console.log(task);
if (task_resp==0){
res.sendStatus(404);
}
else {
res.setHeader(‘Content-Type’, ‘application/json’);
res.json(task);
}
}).catch(function(error) {
res.sendStatus(404);
});
}
else {
res.sendStatus(404);
}
}).catch(function(error) {
res.sendStatus(400);
});
});router.get(‘/destroy’, function(req, res) {res.sendStatus(400);});router.post(‘/update’, function(req, res) {models.Task.update({
name: req.body.name,
dueDate: req.body.dueDate,
priority: req.body.priority },
{ where: { id : req.body.id }} /* where criteria */
).then(function(affectedRows) {
console.log(affectedRows);
res.setHeader(‘Content-Type’, ‘application/json’);
models.Task.find({ where: { id: req.body.id } })
.then(function(task) {
if (task!=null){
res.setHeader(‘Content-Type’, ‘application/json’);
res.json(task);
}
else {
res.sendStatus(404);
}
}).catch(function(error) {
res.sendStatus(400);
});
}).catch(function(error) {
console.log(error);
res.send(error.errors,500);
});
});module.exports = router;

Here it’s the link when you can find all the code use for this tutorial.

https://mega.nz/#!qUtUQL6B!oYWIPTvI_5Pbd7HqiPnA5krr6AdGg334DSvKBN3WmvU

As always thanks for reading!

--

--