An AngularJS Tutorial that will walk you through creating a ToDo Application using a local PouchDb.
- Install brackets (http://brackets.io/)
- Install Emmet Extension (https://github.com/emmetio/emmet/downloads)
- Install Nodejs (http://nodejs.org)
- Open console
mkdir todo-pouch
cd todo-pouch
npm install bower -g
bower init
bower install jquery bootstrap.css angular --save
ower install http://download.pouchdb.com/pouchdb-nightly.min.js --save
touch index.html
index.html - emmet version
html[ng-app="Todo"]>head>title{TODO POUCH}+link[href="/components/bootstrap/css/bootstrap.css"]^body>.container>h1{TODO POUCH}+ng-view^script[src="/components/jquery/jquery.js"]+script[src="/components/angular/angular.js"]+script[src="/components/pouchdb-nightly.min/index.js"]+script[src="/app/ng-app.js"]
index.html
<!doctype html>
<html ng-app="Todo">
<head>
<title>TODO POUCH</title>
<link rel="stylesheet" href="/components/bootstrap/css/bootstrap.css">
</head>
<body>
<div class="container">
<h1>TODO POUCH</h1>
<ng-view></ng-view>
</div>
<script src="/components/jquery/jquery.js"></script>
<script src="/components/angular/angular.js"></script>
<script src="/components/pouchdb-nightly.min/index.js"></script>
</body>
</html>
setup grunt
npm install grunt-cli -g
npm init
touch Gruntfile.js
npm install grunt-contrib-concat grunt-contrib-jshint grunt-contrib-uglify grunt-contrib-watch --save-dev
Paste the following js in Gruntfile.js
var www = __dirname;
var appFiles = [
www + '/app/app.js',
www + '/app/services/*.js',
www + '/app/filters/*.js',
www + '/app/directives/*.js',
www + '/app/controllers/*.js'
];
module.exports = function(grunt) {
grunt.initConfig({
jshint: {
files: appFiles
},
concat: {
app: {
src: appFiles,
dest: www + '/ng-app.js'
}
},
uglify: {
grxnet: {
src: [ www + '/ng-app.js'],
dest: www + '/ng-app.min.js'
}
},
watch: {
scripts: {
files: www + '/app/**/*.js',
tasks: ['jshint','concat'],
options: {
interrupt: true
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', 'jshint concat uglify');
}
Install web server
npm install w3 -g
create app directory
mkdir app
touch app/app.js
mkdir app/controllers
touch app/controllers/main.js
mkdir app/templates
touch app/templates/main.html
open app/app.js and setup the application
angular.module('Todo', [])
.config(function($routeProvider, $locationProvider) {
'use strict';
$routeProvider
.when('/', {
controller: 'MainCtrl',
templateUrl: '/app/templates/main.html'
});
$locationProvider.html5Mode(true);
});
In the app.js we are adding a config section, in this section we are injected two services. ($routeProvider, $locationProvider).
$routeProvider gives the ability to do routing.
$locationProvider gives us html5 push updates.
open app/controllers/main.js
angular.module('Todo')
.controller('MainCtrl', function($scope) {
});
open app/templates/main.html
<h1>Todo Pouch</h1>
Lets confirm we have everything setup correctly.
Open two console windows
console 1 - make sure you are in the project dir
grunt concat
grunt watch
console 2 - make sure your in the project dir
w3
open browser to http://localhost:3000
If you see Todo Pouch in your browser, then we are setup correctly.
We will step through the declarative template line by line.
<span>{{remaining()}} of {{todos.length}} remaining</span>
Show how many todos of total have not been completed. We are using the ng-bind directive to call $scope.remaining() function to get the number of todos that have not been marked completed. Then we use the ng-bind directive to call the $scope.todos.length to get the total count of todo items.
[<a ng-href="" ng-click="removeDone()">Remove done</a>]
Give the user the ability to remove all done tasks from the list. Here we are using an anchor element and a ng-click attribute to map the anchor click event to the $scope.removeDone function.
<ul class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span ng-class="{{done: todo.done">{{todo.text}}</span>
</li>
</ul>
Create an unorded list of todo tasks, for each line item
we want to provide a checkbox with an attribute ng-model
assigned
to $scope.todo.done and map the input ng-click attribute to $scope.updateTodo(todo).
Next we want to use the ng-class directive to add the done
class to the span element if todo.done === true
. And use the ng-bind directive to show the todo task.
<form ng-submit="addTodo()">
<input type="text" ng-model="todoText" size="30"
placeholder="add new todo here">
<input class="btn btn-primary" type="submit" value="add">
</form>
Finally, we want to add a form with a directive ng-submit
, which is assigned to $scope.addTodo()
. In the form, we are assigning the input element ng-model
to $scope.todoText.
Open the browser and you should now see the total text and input form.
Lets wire the view to the controller and models.
open main.js and add the following:
$scope.todos = [];
initialize the $scope.todos array.
$scope.addTodo = function() {
var newTodo = {
_id: Math.uuid(),
text: $scope.todoText,
done: false
};
$scope.todos.push(newTodo);
$scope.todoText = '';
};
Add todo function
$scope.removeDone = function() {
// backup array
var oldTodos = $scope.todos;
// reset to an empty array
$scope.todos = [];
// loop through the old values
angular.forEach(oldTodos, function(todo) {
// if todo is marked as done, or checked.
if (!todo.done) {
// add todo to the array
$scope.todos.push(todo);
}
else {
// remove todo from array
$scope.removeTodo(todo);
}
});
};
Remove all items that are marked as done.
$scope.remaining = function() {
var count = 0;
angular.forEach($scope.todos, function(todo) {
count += todo.done ? 0 : 1;
});
return count;
};
add the remaining function to the controller. Now we should see 0 of 0 remaining.
- Create Pouch Service
mkdir app/services
touch app/services/pouch.js
pouch.js
angular.module('Todo')
.value('$pouch', Pouch('idb://todos'));
- Include in the controller
main.js
replace
.controller('MainCtrl', function($scope) {
with
.controller('MainCtrl', function($scope, $pouch) {
- On Add Todo save to pouch
$scope.addTodo = function() {
var newTodo = {
_id: Math.uuid(),
text: $scope.todoText,
done: false
};
$scope.todos.push(newTodo);
$scope.todoText = '';
$pouch.post(newTodo, function(err, res) {
if (err) { console.log(err); }
newTodo._id = res.id;
newTodo._rev = res.rev;
});
};
$pouch.allDocs({include_docs: true}, function(err, response) {
$scope.$apply(function() {
response.rows.forEach(function(row) {
$scope.todos.push(row.doc);
});
});
});
$scope.removeTodo = function(todo) {
$pouch.remove(todo);
};
$scope.updateTodo = function(todo) {
$pouch.put(todo);
};