├── .bowerrc
├── .gitignore
├── README.md
├── bower.json
├── gulpfile.js
├── package.json
├── plugins
└── .gitignore
└── www
├── config.xml
├── css
└── app.css
├── index.html
├── js
├── app-ng-pouchdb.js
├── ng-pouchdb-collection.js
├── v1.js
├── v2.js
├── v3.js
├── v4.js
├── v5.js
└── v6.js
├── res
├── ios
│ └── icon.png
└── screens
│ └── ios
│ ├── Default-568h@2x~iphone.png
│ └── Default@2x~iphone.png
├── v1.html
├── v2.html
├── v3.html
├── v4.html
├── v5.html
└── v6.html
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "www/lib"
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .*.sw*
2 | *.keystore
3 | node_modules
4 | www/lib
5 | .idea
6 | .DS_Store
7 | tmp
8 | platforms
9 |
10 |
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ionic-pouchdb-todo
2 | ==========================
3 |
4 | This is an older example of the use of [ng-pouchdb](https://github.com/danielzen/ng-pouchdb).
5 |
6 | Please see the latest example of this library in use at http://github.com/danielzen/todo-ng-pouchdb
7 |
8 | This is a 4-way data-binding library, in action using a simple Ionic Todo app with a PouchDb local storage backend configured to sync with a CouchDb installation. This is a demo of offline functionality with server synchronization. And is part of my Offline data synchronization talk.
9 | Slides available at: [http://zndg.tl/ng-pouchdb](http://zndg.tl/ng-pouchdb)
10 |
11 | You can watch me demo building an early version of the app at
12 | [FITC Spotlight: AngularJS](http://youtu.be/6ecuA-pOev0?t=14m9s) in Toronto.
13 |
14 | This repository has multiple releases you can download or tags you can checkout to see the incremental building
15 | of the application. Only the final version using the ng-pouchdb library.
16 |
17 | ## Installation
18 |
19 | You may need to install bower globally with `npm install -g bower` before running `bower install`, to download
20 | the necessary required frontend libraries. You may need to do a `npm -g install bower`, if you haven't already.
21 |
22 | ## Run the App
23 |
24 | You can `cd` into the `www` directory and run
25 |
26 | ```bash
27 | python -m SimpleHTTPServer 8000
28 | ```
29 | If you're using Python 3.x or higher
30 |
31 | ```bash
32 | python -m http.server 8000
33 | ```
34 |
35 |
36 | You can then just open [http://localhost:8000/index.html](http://localhost:8000/index.html) in a browser.
37 |
38 | Personally I use WebStorm which has a built in server. From a JetBrains product, you can select "View...", "Open in Browser" on index.html.
39 |
40 | The final version of this demo requires you to [download and install CouchDb](http://couchdb.apache.org/#download), which runs on port 5984. And, [enable CORS](http://docs.couchdb.org/en/1.6.1/config/http.html#cross-origin-resource-sharing).
41 |
42 | ## iOS version
43 |
44 | However, to run this as a mobile application in iOS emulator,
45 | do the following to setup :
46 |
47 | ```bash
48 | $ cd ionic-pouchdb-todo
49 | $ sudo npm install -g cordova ionic gulp
50 | ```
51 |
52 | To run in the iPhone Simulator:
53 |
54 | ```bash
55 | ionic platform add ios
56 | ionic build ios
57 | ionic emulate ios
58 | ```
59 |
60 | ## Building Out & Updating Ionic or PouchDb
61 |
62 | To update to a new version of Ionic, open bower.json and change the version listed there.
63 |
64 | For example, to update from version `1.0.0-beta.12` to `1.0.0-beta.13`, open bower.json and change this:
65 |
66 | ```
67 | "ionic": "driftyco/ionic-bower#1.0.0-beta.12"
68 | ```
69 |
70 | After saving the update to bower.json file, run `bower install`.
71 |
72 | I was a little forward thinking adding the `package.json` file.
73 | To continue working on this repository, adding tests, using SASS, you can
74 |
75 | ```bash
76 | $ npm install
77 | $ gulp install
78 | ```
79 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ionic PouchDb Todo",
3 | "private": "true",
4 | "devDependencies": {
5 | "ionic": "driftyco/ionic-bower#1.0.0-beta.13"
6 | },
7 | "dependencies": {
8 | "pouchdb": "~3",
9 | "angular-pouchdb": "0.1"
10 | },
11 | "resolutions": {
12 | "angular": "~1.3",
13 | "angular-ui-router": "~0.2",
14 | "angular-animate": "~1.2",
15 | "angular-sanitize": "~1.2",
16 | "pouchdb": "~3"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var gutil = require('gulp-util');
3 | var bower = require('bower');
4 | var concat = require('gulp-concat');
5 | var sass = require('gulp-sass');
6 | var minifyCss = require('gulp-minify-css');
7 | var rename = require('gulp-rename');
8 | var sh = require('shelljs');
9 |
10 | var paths = {
11 | sass: ['./scss/**/*.scss']
12 | };
13 |
14 | gulp.task('default', ['sass']);
15 |
16 | gulp.task('sass', function(done) {
17 | gulp.src('./scss/ionic.app.scss')
18 | .pipe(sass())
19 | .pipe(gulp.dest('./www/css/'))
20 | .pipe(minifyCss({
21 | keepSpecialComments: 0
22 | }))
23 | .pipe(rename({ extname: '.min.css' }))
24 | .pipe(gulp.dest('./www/css/'))
25 | .on('end', done);
26 | });
27 |
28 | gulp.task('watch', function() {
29 | gulp.watch(paths.sass, ['sass']);
30 | });
31 |
32 | gulp.task('install', ['git-check'], function() {
33 | return bower.commands.install()
34 | .on('log', function(data) {
35 | gutil.log('bower', gutil.colors.cyan(data.id), data.message);
36 | });
37 | });
38 |
39 | gulp.task('git-check', function(done) {
40 | if (!sh.which('git')) {
41 | console.log(
42 | ' ' + gutil.colors.red('Git is not installed.'),
43 | '\n Git, the version control system, is required to download Ionic.',
44 | '\n Download git here:', gutil.colors.cyan('http://git-scm.com/downloads') + '.',
45 | '\n Once git is installed, run \'' + gutil.colors.cyan('gulp install') + '\' again.'
46 | );
47 | process.exit(1);
48 | }
49 | done();
50 | });
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ionic-pouchdb-todo",
3 | "version": "1.1.0",
4 | "description": "An Example of the ng-pouchdb library in action",
5 | "repository" :
6 | {
7 | "type" : "git",
8 | "url" : "http://github.com/danielzen/ionic-pouchdb-todo.git"
9 | },
10 | "dependencies": {
11 | "gulp": "^3.5.6",
12 | "gulp-sass": "^0.7.1",
13 | "gulp-concat": "^2.2.0",
14 | "gulp-minify-css": "^0.3.0",
15 | "gulp-rename": "^1.2.0"
16 | },
17 | "devDependencies": {
18 | "bower": "^1.3",
19 | "gulp-util": "^2.2.14",
20 | "shelljs": "^0.3.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danielzen/ionic-pouchdb-todo/76c8c591ea9565951e6a448940df716b1c0d6182/plugins/.gitignore
--------------------------------------------------------------------------------
/www/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | IonPouch Todo
4 |
5 | An Ionic Framework TODO demo with a PouchDB sync backend
6 |
7 |
8 | Zen Digital
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/www/css/app.css:
--------------------------------------------------------------------------------
1 | .complete {
2 | text-decoration: line-through;
3 | color: grey;
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | v7:ToDo - ng-pouchdb
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Todos
32 |
33 |
36 |
39 |
40 |
41 |
42 |
45 |
46 | {{task.title}}
47 |
48 | Edit
49 |
50 |
51 | Delete
52 |
53 |
54 |
55 |
56 |
57 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/www/js/app-ng-pouchdb.js:
--------------------------------------------------------------------------------
1 | angular.module('todo', ['ionic','pouchdb'])
2 | .controller('TodoCtrl', function($scope, $ionicModal, $ionicPopup, $ionicListDelegate, pouchCollection) {
3 |
4 |
5 | ////////////////////////
6 | // Replace PouchDb database calls with pouchCollection
7 | ////////////////////////
8 | $scope.todos = pouchCollection('todoslib');
9 |
10 | $scope.online = false;
11 | $scope.toggleOnline = function() {
12 | $scope.online = !$scope.online;
13 | if ($scope.online) { // Read http://pouchdb.com/api.html#sync
14 | ////////////////////////
15 | // sync to CouchDb with pouchCollection reference
16 | ////////////////////////
17 | $scope.sync = $scope.todos.$db.replicate.sync('http://127.0.0.1:5984/todoslib', {live: true})
18 | .on('error', function (err) {
19 | console.log("Syncing stopped");
20 | console.log(err);
21 | });
22 | } else {
23 | $scope.sync.cancel();
24 | }
25 | };
26 |
27 | $scope.completionChanged = function(task) {
28 | task.completed = !task.completed;
29 | $scope.todos.$update(task);
30 | };
31 |
32 | $scope.delete = function(task) {
33 | $scope.todos.$remove(task);
34 | };
35 |
36 | $scope.editTitle = function (task) {
37 | var scope = $scope.$new(true);
38 | scope.data = { response: task.title } ;
39 | $ionicPopup.prompt({
40 | title: 'Edit task:',
41 | scope: scope,
42 | buttons: [
43 | { text: 'Cancel', onTap: function(e) { return false; } },
44 | {
45 | text: 'Save',
46 | type: 'button-positive',
47 | onTap: function(e) {
48 | return scope.data.response
49 | }
50 | },
51 | ]
52 | }).then(function (newTitle) {
53 | if (newTitle && newTitle != task.title) {
54 | task.title = newTitle;
55 | $scope.todos.$update(task);
56 | }
57 | $ionicListDelegate.closeOptionButtons();
58 | });
59 | };
60 |
61 | // Create our modal
62 | $ionicModal.fromTemplateUrl('new-task.html', function(modal) {
63 | $scope.taskModal = modal;
64 | }, {
65 | scope: $scope
66 | });
67 |
68 | $scope.createTask = function(task) {
69 | task.completed = false;
70 | $scope.todos.$add(task);
71 | console.log("Added "+task.title+" to todos");
72 | $scope.taskModal.hide();
73 | };
74 |
75 | $scope.newTask = function() {
76 | $scope.taskModal.show();
77 | };
78 |
79 | $scope.closeNewTask = function() {
80 | $scope.taskModal.hide();
81 | };
82 |
83 | });
84 |
--------------------------------------------------------------------------------
/www/js/ng-pouchdb-collection.js:
--------------------------------------------------------------------------------
1 | angular.module('pouchdb')
2 |
3 | .factory('pouchCollection', ['$timeout', 'pouchdb', function($timeout, pouchdb) {
4 |
5 | /**
6 | * @class item in the collection
7 | * @param item
8 | * @param {int} index position of the item in the collection
9 | *
10 | * @property {String} _id unique identifier for this item within the collection
11 | * @property {int} $index position of the item in the collection
12 | */
13 | function PouchDbItem(item, index) {
14 | this.$index = index;
15 | angular.extend(this, item);
16 | }
17 |
18 | /**
19 | * create a pouchCollection
20 | * @param {String} collectionUrl The pouchdb url where the collection lives
21 | * @return {Array} An array that will hold the items in the collection
22 | */
23 | return function(collectionUrl) {
24 | var collection = [];
25 | var indexes = {};
26 | var db = collection.$db = pouchdb.create(collectionUrl);
27 |
28 | function getIndex(prevId) {
29 | return prevId ? indexes[prevId] + 1 : 0;
30 | }
31 |
32 | function addChild(index, item) {
33 | indexes[item._id] = index;
34 | collection.splice(index,0,item);
35 | console.log('added: ', index, item);
36 | }
37 |
38 | function removeChild(id) {
39 | var index = indexes[id];
40 |
41 | // Remove the item from the collection
42 | collection.splice(index, 1);
43 | indexes[id] = undefined;
44 |
45 | console.log('removed: ', id);
46 | }
47 |
48 | function updateChild (index, item) {
49 | collection[index] = item;
50 | console.log('changed: ', index, item);
51 | }
52 |
53 | function moveChild (from, to, item) {
54 | collection.splice(from, 1);
55 | collection.splice(to, 0, item);
56 | updateIndexes(from, to);
57 | console.log('moved: ', from, ' -> ', to, item);
58 | }
59 |
60 | function updateIndexes(from, to) {
61 | var length = collection.length;
62 | to = to || length;
63 | if ( to > length ) { to = length; }
64 | for(index = from; index < to; index++) {
65 | var item = collection[index];
66 | item.$index = indexes[item._id] = index;
67 | }
68 | }
69 |
70 | db.changes({live: true, onChange: function(change) {
71 | if (!change.deleted) {
72 | db.get(change.id).then(function (data){
73 | if (indexes[change.id]==undefined) { // CREATE / READ
74 | addChild(collection.length, new PouchDbItem(data, collection.length)); // Add to end
75 | updateIndexes(0);
76 | } else { // UPDATE
77 | var index = indexes[change.id];
78 | var item = new PouchDbItem(data, index);
79 | updateChild(index, item);
80 | }
81 | });
82 | } else { //DELETE
83 | removeChild(change.id);
84 | updateIndexes(indexes[change.id]);
85 | }
86 | }});
87 |
88 | collection.$add = function(item) {
89 | db.post(angular.copy(item)).then(
90 | function(res) {
91 | item._rev = res.rev;
92 | item._id = res.id;
93 | }
94 | );
95 | };
96 | collection.$remove = function(itemOrId) {
97 | var item = angular.isString(itemOrId) ? collection[itemOrId] : itemOrId;
98 | db.remove(item)
99 | };
100 |
101 | collection.$update = function(itemOrId) {
102 | var item = angular.isString(itemOrId) ? collection[itemOrId] : itemOrId;
103 | var copy = {};
104 | angular.forEach(item, function(value, key) {
105 | if (key.indexOf('$') !== 0) {
106 | copy[key] = value;
107 | }
108 | });
109 | db.get(item._id).then(
110 | function (res) {
111 | db.put(copy, res._rev);
112 | }
113 | );
114 | };
115 |
116 | return collection;
117 | };
118 | }]);
119 |
--------------------------------------------------------------------------------
/www/js/v1.js:
--------------------------------------------------------------------------------
1 | angular.module('todo', ['ionic'])
2 | .controller('TodoCtrl', function($scope) {
3 | // Initialize tasks
4 | $scope.tasks =
5 | [
6 | {title: "First", completed: true},
7 | {title: "Second", completed: false},
8 | {title: "Third", completed: false},
9 | ];
10 |
11 | });
12 |
--------------------------------------------------------------------------------
/www/js/v2.js:
--------------------------------------------------------------------------------
1 | angular.module('todo', ['ionic'])
2 | .controller('TodoCtrl', function($scope) {
3 | // Initialize tasks
4 | $scope.tasks =
5 | [
6 | {title: "First", completed: true},
7 | {title: "Second", completed: false},
8 | {title: "Third", completed: false},
9 | ];
10 |
11 | ////////////////////////
12 | // TOGGLE TASK COMPLETED
13 | ////////////////////////
14 | $scope.completionChanged = function(task) {
15 | task.completed = !task.completed;
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/www/js/v3.js:
--------------------------------------------------------------------------------
1 | angular.module('todo', ['ionic'])
2 | .controller('TodoCtrl', function($scope, $ionicModal) {
3 | // Initialize tasks
4 | ////////////////////////
5 | // tasks starts empty
6 | ////////////////////////
7 | $scope.tasks = [];
8 |
9 | $scope.completionChanged = function(task) {
10 | task.completed = !task.completed;
11 | };
12 |
13 | ////////////////////////
14 | // Use $ionicModal to put 'taskModal' in $scope
15 | ////////////////////////
16 | $ionicModal.fromTemplateUrl('new-task.html', function(modal) {
17 | $scope.taskModal = modal;
18 | }, {
19 | scope: $scope
20 | });
21 |
22 | ////////////////////////
23 | // push new task onto tasks array
24 | ////////////////////////
25 | $scope.createTask = function(task) {
26 | task.completed = false;
27 | $scope.tasks.push(angular.copy(task));
28 | task.title = "";
29 | $scope.taskModal.hide();
30 | };
31 |
32 | ////////////////////////
33 | // show taskModal for newTask
34 | ////////////////////////
35 | $scope.newTask = function() {
36 | $scope.taskModal.show();
37 | };
38 |
39 | ////////////////////////
40 | // hide taskModal to closeNewTask
41 | ////////////////////////
42 | $scope.closeNewTask = function() {
43 | $scope.taskModal.hide();
44 | };
45 |
46 | });
47 |
--------------------------------------------------------------------------------
/www/js/v4.js:
--------------------------------------------------------------------------------
1 | angular.module('todo', ['ionic'])
2 | ////////////////////////
3 | // Simple PouchDB synchronization factory
4 | ////////////////////////
5 | .factory('todoDb', function() {
6 | var db = new PouchDB('todos');
7 | return db;
8 | })
9 | .controller('TodoCtrl', function($scope, $ionicModal, todoDb) { // inject todoDb factory
10 | // Initialize tasks
11 | $scope.tasks = [];
12 |
13 | $scope.completionChanged = function(task) {
14 | task.completed = !task.completed;
15 | this.update(task);
16 | };
17 |
18 | ////////////////////////
19 | // http://pouchdb.com/api.html#changes
20 | // list of changes to docs in todoDb
21 | // modify ng-model accordingly
22 | ////////////////////////
23 | todoDb.changes({
24 | live: true,
25 | onChange: function (change) {
26 | if (!change.deleted) { // IF THE CHANGE IS A DELETE, IGNORE
27 | // GET THE doc (a task) from the change.id
28 | todoDb.get(change.id, function(err, doc) {
29 | if (err) console.log(err); ////////////
30 | $scope.$apply(function() { // UPDATE //
31 | // INEFFICIENTLY FIND task THAT HAS CHANGED
32 | for (var i = 0; i < $scope.tasks.length; i++) {
33 | if ($scope.tasks[i]._id === doc._id) {
34 | // REPLACE THE TASK WITH FETCHED doc
35 | $scope.tasks[i] = doc;
36 | return;
37 | } //////////////////////////////////////////
38 | } // IF UNIQUE doc._id NOT FOUND ADD task //
39 | $scope.tasks.push(doc);
40 | });
41 | })
42 | }
43 | }
44 | });
45 |
46 | ////////////////////////
47 | // UPDATE task IN POUCHDB
48 | ////////////////////////
49 | $scope.update = function (task) {
50 | todoDb.get(task._id, function (err, doc) {
51 | if (err) {
52 | console.log(err);
53 | } else {
54 | todoDb.put(angular.copy(task), doc._rev, function (err, res) {
55 | if (err) console.log(err);
56 | });
57 | }
58 | });
59 | };
60 |
61 | // Create our modal
62 | $ionicModal.fromTemplateUrl('new-task.html', function(modal) {
63 | $scope.taskModal = modal;
64 | }, {
65 | scope: $scope
66 | });
67 |
68 | $scope.createTask = function(task) {
69 | task.completed = false;
70 | ////////////////////////
71 | // CREATE task IN POUCHDB
72 | ////////////////////////
73 | todoDb.post(angular.copy(task), function(err, res) {
74 | if (err) console.log(err)
75 | task.title = "";
76 | });
77 | $scope.taskModal.hide();
78 | };
79 |
80 | $scope.newTask = function() {
81 | $scope.taskModal.show();
82 | };
83 |
84 | $scope.closeNewTask = function() {
85 | $scope.taskModal.hide();
86 | };
87 |
88 | });
89 |
--------------------------------------------------------------------------------
/www/js/v5.js:
--------------------------------------------------------------------------------
1 | angular.module('todo', ['ionic'])
2 | // Simple PouchDB factory
3 | .factory('todoDb', function() {
4 | var db = new PouchDB('todos');
5 | return db;
6 | }) // inject ionicPopup & ionicListDelegate
7 | .controller('TodoCtrl', function($scope, $ionicModal, todoDb, $ionicPopup, $ionicListDelegate) {
8 | // Initialize tasks
9 | $scope.tasks = [];
10 |
11 | $scope.completionChanged = function(task) {
12 | task.completed = !task.completed;
13 | $scope.update(task);
14 | };
15 |
16 | // list changes to PouchDb database
17 | todoDb.changes({
18 | live: true,
19 | onChange: function (change) {
20 | if (!change.deleted) {
21 | todoDb.get(change.id, function(err, doc) {
22 | if (err) console.log(err);
23 | $scope.$apply(function() { //UPDATE
24 | for (var i = 0; i < $scope.tasks.length; i++) {
25 | if ($scope.tasks[i]._id === doc._id) {
26 | $scope.tasks[i] = doc;
27 | return;
28 | }
29 | } // CREATE / READ
30 | $scope.tasks.push(doc);
31 | });
32 | }) //////////////////////
33 | } else { // if change.delete //
34 | $scope.$apply(function () {
35 | for (var i = 0; i<$scope.tasks.length; i++) {
36 | if ($scope.tasks[i]._id === change.id) {
37 | $scope.tasks.splice(i,1);
38 | }
39 | }
40 | })
41 | }
42 | }
43 | });
44 |
45 | $scope.update = function (task) {
46 | todoDb.get(task._id, function (err, doc) {
47 | if (err) {
48 | console.log(err);
49 | } else {
50 | todoDb.put(angular.copy(task), doc._rev, function (err, res) {
51 | if (err) console.log(err);
52 | });
53 | }
54 | });
55 | };
56 |
57 | ////////////////////////
58 | // DELETE task IN POUCHDB
59 | ////////////////////////
60 | $scope.delete = function(task) {
61 | todoDb.get(task._id, function (err, doc) {
62 | todoDb.remove(doc, function (err, res) {});
63 | });
64 | };
65 |
66 | ////////////////////////
67 | // EDIT task.title with ionicPopup
68 | ////////////////////////
69 | $scope.editTitle = function (task) {
70 | var scope = $scope.$new(true);
71 | scope.data = {response: task.title };
72 | $ionicPopup.prompt({
73 | title: 'Edit task:',
74 | scope: scope,
75 | buttons: [
76 | { text: 'Cancel', onTap: function(e) { return false; } },
77 | {
78 | text: 'Save',
79 | type: 'button-positive',
80 | onTap: function(e) {
81 | return scope.data.response
82 | }
83 | },
84 | ]
85 | }).then(function (newTitle) {
86 | if (newTitle && newTitle != task.title) {
87 | task.title = newTitle;
88 | $scope.update(task);
89 | }
90 | $ionicListDelegate.closeOptionButtons();
91 | });
92 | };
93 |
94 | // Create our modal
95 | $ionicModal.fromTemplateUrl('new-task.html', function(modal) {
96 | $scope.taskModal = modal;
97 | }, {
98 | scope: $scope
99 | });
100 |
101 | $scope.createTask = function(task) {
102 | task.completed = false;
103 | todoDb.post(angular.copy(task), function(err, res) {
104 | if (err) console.log(err)
105 | task.title = "";
106 | });
107 | $scope.taskModal.hide();
108 | };
109 |
110 | $scope.newTask = function() {
111 | $scope.taskModal.show();
112 | };
113 |
114 | $scope.closeNewTask = function() {
115 | $scope.taskModal.hide();
116 | };
117 |
118 | });
119 |
--------------------------------------------------------------------------------
/www/js/v6.js:
--------------------------------------------------------------------------------
1 | angular.module('todo', ['ionic'])
2 | // Simple PouchDB factory
3 | .factory('todoDb', function() {
4 | var db = new PouchDB('todos');
5 | return db;
6 | })
7 | .controller('TodoCtrl', function($scope, $ionicModal, todoDb, $ionicPopup, $ionicListDelegate) {
8 | // Initialize tasks
9 | $scope.tasks = [];
10 |
11 | ////////////////////////
12 | // Online sync to CouchDb
13 | ////////////////////////
14 | $scope.online = false;
15 | $scope.toggleOnline = function() {
16 | $scope.online = !$scope.online;
17 | if ($scope.online) { // Read http://pouchdb.com/api.html#sync
18 | $scope.sync = todoDb.sync('http://127.0.0.1:5984/todos', {live: true})
19 | .on('error', function (err) {
20 | console.log("Syncing stopped");
21 | console.log(err);
22 | });
23 | } else {
24 | $scope.sync.cancel();
25 | }
26 | };
27 |
28 | $scope.completionChanged = function(task) {
29 | task.completed = !task.completed;
30 | $scope.update(task);
31 | };
32 |
33 | todoDb.changes({
34 | live: true,
35 | onChange: function (change) {
36 | if (!change.deleted) {
37 | todoDb.get(change.id, function(err, doc) {
38 | if (err) console.log(err);
39 | $scope.$apply(function() { //UPDATE
40 | for (var i = 0; i < $scope.tasks.length; i++) {
41 | if ($scope.tasks[i]._id === doc._id) {
42 | $scope.tasks[i] = doc;
43 | return;
44 | }
45 | } // CREATE / READ
46 | $scope.tasks.push(doc);
47 | });
48 | })
49 | } else { //DELETE
50 | $scope.$apply(function () {
51 | for (var i = 0; i<$scope.tasks.length; i++) {
52 | if ($scope.tasks[i]._id === change.id) {
53 | $scope.tasks.splice(i,1);
54 | }
55 | }
56 | })
57 | }
58 | }
59 | });
60 |
61 | $scope.update = function (task) {
62 | todoDb.get(task._id, function (err, doc) {
63 | if (err) {
64 | console.log(err);
65 | } else {
66 | todoDb.put(angular.copy(task), doc._rev, function (err, res) {
67 | if (err) console.log(err);
68 | });
69 | }
70 | });
71 | };
72 |
73 | $scope.delete = function(task) {
74 | todoDb.get(task._id, function (err, doc) {
75 | todoDb.remove(doc, function (err, res) {});
76 | });
77 | };
78 |
79 | $scope.editTitle = function (task) {
80 | var scope = $scope.$new(true);
81 | scope.data = { response: task.title } ;
82 | $ionicPopup.prompt({
83 | title: 'Edit task:',
84 | scope: scope,
85 | buttons: [
86 | { text: 'Cancel', onTap: function(e) { return false; } },
87 | {
88 | text: 'Save',
89 | type: 'button-positive',
90 | onTap: function(e) {
91 | return scope.data.response
92 | }
93 | },
94 | ]
95 | }).then(function (newTitle) {
96 | if (newTitle && newTitle != task.title) {
97 | task.title = newTitle;
98 | $scope.update(task);
99 | }
100 | $ionicListDelegate.closeOptionButtons();
101 | });
102 | };
103 |
104 | // Create our modal
105 | $ionicModal.fromTemplateUrl('new-task.html', function(modal) {
106 | $scope.taskModal = modal;
107 | }, {
108 | scope: $scope
109 | });
110 |
111 | $scope.createTask = function(task) {
112 | task.completed = false;
113 | todoDb.post(angular.copy(task), function(err, res) {
114 | if (err) console.log(err)
115 | task.title = "";
116 | });
117 | $scope.taskModal.hide();
118 | };
119 |
120 | $scope.newTask = function() {
121 | $scope.taskModal.show();
122 | };
123 |
124 | $scope.closeNewTask = function() {
125 | $scope.taskModal.hide();
126 | };
127 |
128 | });
129 |
--------------------------------------------------------------------------------
/www/res/ios/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danielzen/ionic-pouchdb-todo/76c8c591ea9565951e6a448940df716b1c0d6182/www/res/ios/icon.png
--------------------------------------------------------------------------------
/www/res/screens/ios/Default-568h@2x~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danielzen/ionic-pouchdb-todo/76c8c591ea9565951e6a448940df716b1c0d6182/www/res/screens/ios/Default-568h@2x~iphone.png
--------------------------------------------------------------------------------
/www/res/screens/ios/Default@2x~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danielzen/ionic-pouchdb-todo/76c8c591ea9565951e6a448940df716b1c0d6182/www/res/screens/ios/Default@2x~iphone.png
--------------------------------------------------------------------------------
/www/v1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | v1:ToDo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Todos
24 |
25 |
26 |
27 |
29 | {{task.title}}
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/www/v2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | v2:ToDo - Toggle
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Todos
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 | {{task.title}}
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/www/v3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | v3:ToDo - New Tasks
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Todos
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
36 | {{task.title}}
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/www/v4.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | v4:ToDo - PouchDb
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Todos
29 |
32 |
33 |
34 |
35 |
38 | {{task.title}}
39 |
40 |
41 |
42 |
43 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/www/v5.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | v5:ToDo - Edit/Delete
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Todos
26 |
29 |
30 |
31 |
32 |
35 |
36 | {{task.title}}
37 |
38 |
39 |
40 |
41 | Edit
42 |
43 |
44 | Delete
45 |
46 |
47 |
48 |
49 |
50 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/www/v6.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | v6:ToDo - online
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Todos
26 |
27 |
28 |
29 |
32 |
35 |
36 |
37 |
38 |
41 |
42 | {{task.title}}
43 |
44 | Edit
45 |
46 |
47 | Delete
48 |
49 |
50 |
51 |
52 |
53 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------