├── .gitignore ├── README.md ├── bower.json ├── dist ├── ngdb.js └── ngdb.min.js ├── package.json └── src ├── ngdb.js ├── ngdbCache.service.js ├── ngdbDataConverter.service.js ├── ngdbQuery.service.js ├── ngdbQueryBuilder.service.js ├── ngdbRepository.service.js └── ngdbUtils.service.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Depreciation notice : this repository is no longer maintained. 2 | You can find other good lib such as : 3 | - localForage : https://github.com/localForage/localForage 4 | - angular localForage : https://github.com/ocombe/angular-localForage 5 | - pouch db : https://pouchdb.com/ (with sync capatibilities) 6 | 7 | # ngDatabase 8 | ngDatabase is a light, very easy to use and powerful __storage__ solution for your __[Ionic](http://ionicframework.com/)__ apps. Take advantage of unlimited storage size, data binding, very flexible data management and more. 9 | 10 | # Quick links 11 | 12 | * __Get started__ 13 | * [Quick guide] (#quick-guide) 14 | * [Installation] (#installation) 15 | * __Repositories__ 16 | * [Create Repositories] (#create) 17 | * [Get Repositories] (#get) 18 | * __Data operation__ 19 | * [Add data] (#add) 20 | * [Get data] (#get) 21 | * [Update data] (#update) 22 | * [Delete data] (#delete) 23 | * __Data selection__ 24 | * [Set order](#order) 25 | * [Set standards](#standards) 26 | * [Set limit](#limit) 27 | * __Data binding__ 28 | * [How it works]() 29 | * [Watch updates]() 30 | * __Low level usage__ 31 | * [Native SQLite syntax] (#native-sqlite-syntax) 32 | 33 | ### Get started 34 | #### Quick guide 35 | Get started in 4 steps by following this guideline. 36 | 37 | * The very first thing you have to do is install ngDatabase : [ngDatabase installation] (#installation) 38 | 39 | * At this point you must launch ngDatabase inside your app and tell him what kind of 'repository' you will used. In ngDatabase a repository is a place where your data will be stored. For exemple if you have to store some user and customer data you will have two repositories severally called 'users' and 'customers'. Check how to create repositories : [Create Repositories] (#create) 40 | 41 | * Now you've got some repositories ready to work. Each time you make operations on your repositories you have to use the _getRepository()_ method to be able to do anything. -> [Get Repositories] (#get) 42 | 43 | * The previous method give you an new instance of a working repository. Now you can make what you want thanks to the 4 methods : [Data operation] (#data-operation) 44 | 45 | * As you can observe we can't do a lot only with these 4 methods. It's the combination between them and 3 others which make the magic. These 3 others are _setBy(), setOrder()_ and _setLimit()_ which define by what criterion the data will be get, add, delete, ... Check it : [Data selection] (#data-selection) 46 | 47 | ### Installation 48 | #### ngCordova and cordovaSQLite 49 | First, install ndCordova to your project (http://ngcordova.com/docs/install/) : 50 | ```shell 51 | bower install ngCordova 52 | ``` 53 | Don't forget to include the ng-cordova.js file and add ngCordova in your app dependencies : 54 | ```html 55 | 56 | ``` 57 | ```javascript 58 | angular.module('myApp', ['ngCordova']); 59 | ``` 60 | 61 | Then, add the cordovaSQLite plugin : 62 | ```shell 63 | cordova plugin add https://github.com/litehelpers/Cordova-sqlite-storage.git 64 | ``` 65 | #### ngDatabase 66 | ```shell 67 | bower install ng-database #bower 68 | npm install ng-database #NPM 69 | ``` 70 | 71 | Include the ng-database js file in your project : 72 | ```html 73 | 74 | ``` 75 | Then include ngDatabase dependency : 76 | ```javascript 77 | angular.module('myApp', ['ngCordova', 'ngDatabase']); 78 | ``` 79 | 80 | # API 81 | 82 | ##### Important note : all of ngDatabase method must be used when the _deviceready_ event fired. 83 | 84 | ### Repositories 85 | #### Create 86 | ##### Prototype 87 | ```javascript 88 | ngdbProvider setRepository(string repositoryName, object repositorySchema) 89 | ``` 90 | ##### Description 91 | A repository is a kind of bag which define your data schema. It's typically an object where each key-value pair correspond respectively to the name of your data and his type (see bellow). This operation is done in the config step of your app. 92 | For exemple, if you have to manage users and pictures in your app your repositories could look like that : 93 | ```javascript 94 | app.config(function(ngdbProvider) { 95 | var usersRepository = { 96 | id: 'ID', 97 | pictures_id:'NUMBER' 98 | name: 'STRING', 99 | born: 'DATE' 100 | }; 101 | 102 | var pictures = { 103 | id: 'ID', 104 | pictures: 'OBJECT' 105 | }; 106 | 107 | ngdbProvider 108 | .setRepository('users', usersRepository) 109 | .setRepository('pictures', picturesRepository); 110 | }); 111 | ``` 112 | 113 | * __ID__ : special integer type which is incremented at each insertion 114 | * __STRING__ : can store string such as text 115 | * __NUMBER__ : an integer or floating number 116 | * __BOOLEAN__ : _true_ or _false_ values 117 | * __OBJECT__ : a javascript object 118 | * __ARRAY__ : a javascript array 119 | * __DATE__ : a date (must be an instance of Date()) 120 | 121 | #### Get 122 | ##### Prototype 123 | ```javascript 124 | ngdb getRepository(string repositoryName) 125 | ``` 126 | ##### Description 127 | This method allow you to make operations in the specified _repositoryName_. 128 | Use it to add, delete, update... 129 | ```javascript 130 | myApp.controller('myCtrl', function(ngdb) { 131 | 132 | var usersRepository = ngdb.getRepository('users'); 133 | var picturesRepository = ngdb.getRepository('pictures'); 134 | 135 | //Make all your operations. 136 | 137 | }); 138 | ``` 139 | 140 | ### Data operation 141 | #### Add 142 | ##### Prototype 143 | ```javascript 144 | promise add(object data) 145 | ``` 146 | ##### Description 147 | This method add some data in a repository. Only the keys that correspond to the mapping defined in the config step will be added. Note that you do not have to convert your data. Just let objects as objects, numbers as numbers, strings as strings, ... 148 | 149 | __Return__ a promise containing an object with the insertion informations (the ID particularly). 150 | 151 | ```javascript 152 | myApp.controller('myCtrl', function(ngdb) { 153 | 154 | var usersRepository = ngdb.getRepository('users'); 155 | var picturesRepository = ngdb.getRepository('pictures'); 156 | 157 | var userToAdd = { 158 | pictures_id: 5, 159 | name: 'Jack', 160 | born: new Date() 161 | }; 162 | var pictureToAdd = { 163 | pictures: {'path1', 'path2'} 164 | }; 165 | 166 | var user = usersRepository.add(userToAdd); 167 | var picture = picturesRepository.add(pictureToAdd); 168 | 169 | user.then(function(result) { 170 | //The insered id 171 | console.log(result.insertId); 172 | }); 173 | 174 | }); 175 | ``` 176 | ### Get 177 | ##### Prototypes 178 | ```javascript 179 | promise get() 180 | promise getOne() 181 | ``` 182 | ##### Description 183 | Get data from repository. 184 | All your data are gived back to the correct type (objects as objects, numbers as numbers, ...) 185 | 186 | __Return__ promise containing an object with the requested data. 187 | 188 | ```javascript 189 | myApp.controller('myCtrl', function(ngdb) { 190 | 191 | var usersRepository = ngdb.getRepository('users'); 192 | var picturesRepository = ngdb.getRepository('pictures'); 193 | 194 | //Get all users and pictures data 195 | var usersData = usersRepository.get(); 196 | var picturesData = picturesRepository.get(); 197 | 198 | //Get the first user and picture data 199 | var firstUserData = usersRepository.getOne(); 200 | var firstPictureData = pictureRepository.getOne(); 201 | 202 | usersData.then(function(result) { 203 | //Your data is here ! 204 | console.log(result); 205 | }); 206 | 207 | }); 208 | ``` 209 | 210 | #### Update 211 | ##### Prototype 212 | ```javascript 213 | promise update(object data) 214 | ``` 215 | ##### Description 216 | Update the specified _data_. 217 | 218 | __Return__ promise containing an object with informations about the update. 219 | 220 | ```javascript 221 | myApp.controller('myCtrl', function(ngdb) { 222 | 223 | var usersRepository = ngdb.getRepository('users'); 224 | var picturesRepository = ngdb.getRepository('pictures'); 225 | 226 | var usersToUpdate = { 227 | name: 'John Doe', 228 | }; 229 | var picturesToUpdate = { 230 | pictures: {'newPath'} 231 | }; 232 | 233 | //Update all users and pictures data 234 | usersRepository.update(usersToUpdate); 235 | picturesRepository.update(picturesToUpdate); 236 | 237 | }); 238 | ``` 239 | 240 | #### Delete 241 | ##### Prototype 242 | ```javascript 243 | promise delete() 244 | ``` 245 | ##### Description 246 | Delete entries in the repository. 247 | 248 | __Return__ promise containing an object with the informations about the deletion. 249 | 250 | ```javascript 251 | myApp.controller('myCtrl', function(ngdb) { 252 | 253 | var usersRepository = ngdb.getRepository('users'); 254 | var picturesRepository = ngdb.getRepository('pictures'); 255 | 256 | //Delete all users and pictures data 257 | usersRepository.delete(); 258 | picturesRepository.delete(); 259 | 260 | }); 261 | ``` 262 | 263 | ### Data selection 264 | These methods can be chained and must be called before the data operation methods (get, update, ...). 265 | These methods have an influence on the way the data are going to be treated. All of them take an object where the key correspond to the data name previously defined (in the app config step). 266 | 267 | #### Order 268 | ##### Prototype 269 | ```javascript 270 | ngdb setOrder(object order) 271 | ``` 272 | 273 | ##### Description 274 | Order your data by something in ascendent ('ASC' keyword) or descendent ('DESC' keyword) order. 275 | 276 | __Return__ promise containing an object with the requested data. 277 | 278 | ```javascript 279 | myApp.controller('myCtrl', function(ngdb) { 280 | 281 | var usersRepository = ngdb.getRepository('users'); 282 | 283 | //Get all users sorted by name in ascendent order 284 | usersRepository.setOrder({'name': 'ASC'}).get(); 285 | //Get all users sorted by id in descendent order 286 | usersRepository.setOrder({'id': 'DESC'}).get(); 287 | 288 | }); 289 | ``` 290 | 291 | #### Standards 292 | ##### Prototype 293 | ```javascript 294 | ngdb setBy(object conditions) 295 | ``` 296 | 297 | ##### Description 298 | Get, update or delete data according to the equality of the key-value object. 299 | 300 | __Return__ promise containing an object with the requested data. 301 | 302 | ```javascript 303 | myApp.controller('myCtrl', function(ngdb) { 304 | 305 | var usersRepository = ngdb.getRepository('users'); 306 | 307 | //Get all users named 'John' 308 | usersRepository.setBy({'name': 'John'}).get(); 309 | //Get the user with id equal to 1 310 | usersRepository.setOrder({'id': 1}).getOne(); 311 | //Get the user named John with id equal to 1 312 | usersRepository.setBy({'id': 1, 'name': 'John'}).getOne(); 313 | 314 | }); 315 | ``` 316 | 317 | #### Limit 318 | ##### Prototype 319 | ```javascript 320 | ngdb setLimit(int from, int to) 321 | ``` 322 | Take two integer which represent the interval. 323 | 324 | __Return__ promise containing an object with the requested data. 325 | 326 | ```javascript 327 | myApp.controller('myCtrl', function(ngdb) { 328 | 329 | var usersRepository = ngdb.getRepository('users'); 330 | 331 | //Get 0 to 10 first results 332 | usersRepository.setLimit(0, 10).get(); 333 | //Get 10 to 20 first results 334 | usersRepository.setLimit(10, 20).get(); 335 | 336 | }); 337 | ``` 338 | 339 | ### Data binding 340 | 341 | _CURRENT WRITING, VERY SOON AVAILABLE_ 342 | 343 | ### Low level usage 344 | #### Native SQLite syntax 345 | ##### Prototypes 346 | ```javascript 347 | promise query(string query, array bindings) 348 | object fetchAll(object SQLiteResult) 349 | object fetch(object SQLiteResult) 350 | ``` 351 | ##### Description 352 | NGDatabase also allow you to use SQLite as native syntax. 353 | * make() : make an SQL Query 354 | * fetchAll() : fetch all SQLite Query result 355 | * fetch() : fetch one SQLite Query result 356 | 357 | ##### Exemple 358 | 359 | ```javascript 360 | myApp.controller('myCtrl', function(ngdb) { 361 | 362 | var qm = ngdb.getQueryMaker(); 363 | var result = qm.make("SELECT * FROM users WEHRE name = ?", ['John Doe']); 364 | 365 | result.then(function(result) { 366 | result = qm.fetchAll(result); 367 | }); 368 | }); 369 | ``` 370 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-database", 3 | "version": "1.3.5", 4 | "description": "A simple and powerful local storage solution for your Ionic apps.", 5 | "main": "dist/ngdb.js", 6 | "keywords": [ 7 | "ionic", 8 | "sqlite", 9 | "ngDatabase" 10 | ], 11 | "authors": [ 12 | "Antoine Bellion" 13 | ], 14 | "license": "MIT", 15 | "ignore": [ 16 | ".gitignore" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /dist/ngdb.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('ngDatabase', ['ngCordova']) 3 | .constant('NGDB_TYPES', { 4 | ID: 'integer', 5 | STRING: 'text', 6 | NUMBER: 'integer', 7 | BOOLEAN: 'text', 8 | OBJECT: 'text', 9 | ARRAY: 'text', 10 | DATE: 'datetime' 11 | }); 12 | 13 | angular 14 | .module('ngDatabase') 15 | .provider('ngdb', ngdbProvider); 16 | 17 | ngdbProvider.$inject = ['NGDB_TYPES']; 18 | function ngdbProvider(NGDB_TYPES) { 19 | var self = this; 20 | self.repositoriesSchema = {}; 21 | 22 | var _validRepository = function(repositorySchema) { 23 | var isValid = true; 24 | 25 | ngdbUtils().browseObject(repositorySchema, function(type, name) { 26 | isValid = (NGDB_TYPES[type]) ? isValid : false; 27 | }); 28 | 29 | return (isValid) 30 | }; 31 | 32 | self.setRepository = function(repositoryName, repositorySchema) { 33 | if (_validRepository(repositorySchema)) { 34 | repositorySchema['id'] = 'ID'; 35 | 36 | self.repositoriesSchema[repositoryName] = repositorySchema; 37 | } 38 | else { 39 | ngdbUtils().errorHandler("Unable to create '"+repositoryName+"' due to unknown datatype."); 40 | } 41 | 42 | return (self); 43 | }; 44 | 45 | self.$get = ngdbFactory; 46 | 47 | return (self); 48 | } 49 | 50 | ngdbFactory.$inject = ['$q', '$injector', 'ngdbUtils', 'ngdbQuery', 'ngdbCache', 'NGDB_TYPES']; 51 | function ngdbFactory($q, $injector, ngdbUtils, ngdbQuery, ngdbCache, NGDB_TYPES) { 52 | var self = this; 53 | var ngdb = {}; 54 | 55 | /* 56 | ** REPOSITORIES 57 | */ 58 | var _formatRepository = function(repositorySchema) { 59 | var ret = {}; 60 | 61 | ngdbUtils.browseObject(repositorySchema, function(columnType, columnName) { 62 | ret[columnName] = (columnName !== "id") ? NGDB_TYPES[columnType] : 'integer primary key'; 63 | }); 64 | 65 | return (ret); 66 | }; 67 | 68 | ngdb.createRepositories = function() { 69 | var queries = []; 70 | var schema = self.repositoriesSchema; 71 | 72 | ngdbUtils.browseObject(schema, function(table, tableName) { 73 | var columns = []; 74 | table = _formatRepository(table); 75 | 76 | ngdbUtils.browseObject(table, function(columnType, columnName) { 77 | columns.push('`' + columnName + '` ' + columnType); 78 | }); 79 | 80 | queries.push(ngdbQuery.make('CREATE TABLE IF NOT EXISTS `' + tableName + '` (' + columns.join(', ') + ')')); 81 | }); 82 | 83 | return ($q.all(queries)); 84 | }; 85 | 86 | ngdb.getRepository = function(repositoryName, binding) { 87 | var repository = $injector.instantiate(ngdbRepository, { 'ngdbQueryBuilder': $injector.instantiate(ngdbQueryBuilder) }); 88 | var repositorySchema = ngdb.getRepositorySchema(repositoryName); 89 | 90 | repository.ngdbRepositorySetRepository(repositoryName, repositorySchema, binding); 91 | 92 | return (repository); 93 | }; 94 | 95 | ngdb.getRepositorySchema = function(repositoryName) { 96 | return (self.repositoriesSchema[repositoryName] || null); 97 | }; 98 | 99 | ngdb.getQueryMaker = function() { 100 | return (ngdbQuery); 101 | }; 102 | 103 | /* 104 | ** WATCHERS 105 | */ 106 | ngdb.putWatcher = function(value, callback, call) { 107 | var watcherId = ngdbCache.putWatcher(value, callback); 108 | 109 | if (call === true || typeof call === "undefined") { 110 | ngdbCache.callWatcher(value, value); 111 | } 112 | 113 | return (watcherId); 114 | }; 115 | 116 | ngdb.popWatcher = function(watcherId) { 117 | return (ngdbCache.popWatcher(watcherId)); 118 | }; 119 | 120 | return (ngdb.createRepositories(), ngdb); 121 | }; 122 | angular 123 | .module('ngDatabase') 124 | .factory('ngdbQuery', ngdbQuery); 125 | 126 | ngdbQuery.$inject = ['$q', '$cordovaSQLite']; 127 | function ngdbQuery($q, $cordovaSQLite) { 128 | var self = this; 129 | var _db = null; 130 | 131 | var _dbConnexion = function() { 132 | 133 | _db = (window.cordova) ? 134 | $cordovaSQLite.openDB('ngdb.db') : 135 | window.openDatabase('ngdb.db', '1', 'ngdb.db', -1); 136 | 137 | return (_db); 138 | }; 139 | 140 | 141 | self.make = function(query, bindings) { 142 | var deferred = $q.defer(); 143 | bindings = (bindings !== undefined && bindings !== null) ? bindings : []; 144 | 145 | _db.transaction(function(transaction) { 146 | transaction.executeSql(query, bindings, function(transaction, result) { 147 | deferred.resolve(result); 148 | }, function(transaction, error) { 149 | deferred.reject(error); 150 | }); 151 | }); 152 | 153 | return (deferred.promise); 154 | }; 155 | 156 | self.fetchAll = function(result) { 157 | var output = []; 158 | var rows = result.rows.length; 159 | 160 | for (var i = 0; i < rows; i++) { 161 | output.push(result.rows.item(i)); 162 | } 163 | 164 | return (output); 165 | }; 166 | 167 | self.fetch = function(result) { 168 | return ((result.rows.length > 0) ? result.rows.item(0) : null); 169 | }; 170 | 171 | return (_dbConnexion(), self); 172 | } 173 | angular 174 | .module('ngDatabase') 175 | .service('ngdbRepository', ngdbRepository); 176 | 177 | ngdbRepository.$inject = ['$q', '$injector', 'ngdbUtils', 'ngdbQuery', 'ngdbQueryBuilder', 'ngdbCache', 'ngdbDataConverter']; 178 | function ngdbRepository($q, $injector, ngdbUtils, ngdbQuery, ngdbQueryBuilder, ngdbCache, ngdbDataConverter) { 179 | var self = this; 180 | var _binding = true; 181 | var _repositoryName = null; 182 | var _repositorySchema = null; 183 | 184 | /* 185 | ** UTILS METHODS 186 | */ 187 | self.ngdbRepositorySetRepository = function(repositoryName, repositorySchema, binding) { 188 | _repositoryName = repositoryName; 189 | _repositorySchema = repositorySchema; 190 | _binding = (binding === false) ? false : true; 191 | 192 | ngdbQueryBuilder.ngdbQueryBuilderSetRepository(repositoryName); 193 | 194 | return (self); 195 | }; 196 | 197 | var _formatGet = function(result) { 198 | var fetched = ngdbQuery.fetchAll(result); 199 | 200 | fetched && fetched.forEach(function(val, index) { 201 | fetched[index] = ngdbDataConverter.convertDataToGet(val, _repositorySchema); 202 | }); 203 | 204 | return (fetched); 205 | }; 206 | 207 | var _formatGetOne = function(result) { 208 | var fetched = ngdbDataConverter.convertDataToGet(ngdbQuery.fetch(result), _repositorySchema); 209 | 210 | return ((fetched) ? fetched : null); 211 | }; 212 | 213 | var _updateCache = function(promise) { 214 | if (!_binding) { 215 | return (0); 216 | } 217 | 218 | promise.then(function() { 219 | ngdbCache.updateCache(_repositoryName); 220 | }); 221 | }; 222 | 223 | /* 224 | ** USER METHODS 225 | */ 226 | self.get = function() { 227 | var deferred = $q.defer(); 228 | var query = this.buildQuery('SELECT'); 229 | var cache = ngdbCache.getCache(_repositoryName, query, _formatGet); 230 | 231 | if (cache === false) { 232 | var result = ngdbQuery.make(query['query'], query['binds']); 233 | 234 | result.then(function(result) { 235 | var formated = _formatGet(result); 236 | 237 | deferred.resolve(formated); 238 | ngdbCache.putCache(_repositoryName, query, _formatGet, formated); 239 | }, deferred.reject); 240 | } 241 | else { 242 | deferred.resolve(cache); 243 | } 244 | 245 | return (this.resetBuilder(), deferred.promise); 246 | }; 247 | 248 | self.getOne = function() { 249 | var deferred = $q.defer(); 250 | var query = this.setLimit(0, 1).buildQuery('SELECT'); 251 | var cache = ngdbCache.getCache(_repositoryName, query, _formatGetOne); 252 | 253 | if (cache === false) { 254 | var result = ngdbQuery.make(query['query'], query['binds']); 255 | 256 | result.then(function(result) { 257 | var formated = _formatGetOne(result); 258 | 259 | deferred.resolve(formated); 260 | ngdbCache.putCache(_repositoryName, query, _formatGetOne, formated); 261 | }, deferred.reject); 262 | } 263 | else { 264 | deferred.resolve(cache); 265 | } 266 | 267 | return (this.resetBuilder(), deferred.promise); 268 | }; 269 | 270 | self.add = function(data) { 271 | data = ngdbDataConverter.convertDataToAdd(data, _repositorySchema); 272 | var query = this.buildQuery('INSERT', data); 273 | var result = ngdbQuery.make(query['query'], query['binds']); 274 | 275 | _updateCache(result); 276 | return (this.resetBuilder(), result); 277 | }; 278 | 279 | self.update = function(data) { 280 | data = ngdbDataConverter.convertDataToAdd(data, _repositorySchema); 281 | var query = this.buildQuery('UPDATE', data); 282 | var result = ngdbQuery.make(query['query'], query['binds']); 283 | 284 | _updateCache(result); 285 | return (this.resetBuilder(), result); 286 | }; 287 | 288 | self.delete = function() { 289 | var query = this.buildQuery('DELETE'); 290 | var result = ngdbQuery.make(query['query'], query['binds']); 291 | 292 | _updateCache(result); 293 | return (this.resetBuilder(), result); 294 | }; 295 | 296 | angular.extend(self, ngdbQueryBuilder); 297 | 298 | return (self); 299 | } 300 | angular 301 | .module('ngDatabase') 302 | .factory('ngdbCache', ngdbCache); 303 | 304 | ngdbCache.$inject = ['ngdbQuery', 'ngdbUtils']; 305 | function ngdbCache(ngdbQuery, ngdbUtils) { 306 | var self = this; 307 | var _cache = {}; 308 | var _watchers = []; 309 | 310 | /* 311 | ** CACHE UTILS METHODS 312 | */ 313 | var _mergeArray = function(dst, src) { 314 | src && src.forEach(function(val, key) { 315 | dst[key] = src[key]; 316 | }); 317 | dst && dst.forEach(function(val, key) { 318 | if (!src[key]) { 319 | dst.pop(); 320 | } 321 | }); 322 | 323 | return (dst); 324 | }; 325 | 326 | var _mergeObject = function(dst, src) { 327 | src && ngdbUtils.browseObject(src, function(val, key) { 328 | dst[key] = val; 329 | }); 330 | dst && ngdbUtils.browseObject(dst, function(val, key) { 331 | if (!src || !src[key]) { 332 | delete dst[key]; 333 | } 334 | }); 335 | 336 | return (dst); 337 | }; 338 | 339 | var _mergeData = function(dst, src) { 340 | if (src instanceof Array) { 341 | return (_mergeArray(dst, src)); 342 | } 343 | else if (src instanceof Object) { 344 | return (_mergeObject(dst, src)); 345 | } 346 | 347 | return (_mergeObject(dst, src)); 348 | }; 349 | 350 | /* 351 | ** WATCH UTILS METHODS 352 | */ 353 | var _getWatcher = function(value) { 354 | var ret = false; 355 | 356 | _watchers.some(function(watcher) { 357 | return (watcher['value'] === value && (ret = watcher)); 358 | }); 359 | 360 | return ((ret === false) ? false : ret); 361 | }; 362 | 363 | /* 364 | ** CACHE METHODS 365 | */ 366 | self.getCache = function(repositoryName, query, dataFormater) { 367 | var repositoryCache = _cache[repositoryName]; 368 | var tmpDataFormater = dataFormater.toString(); 369 | var tmpQuery = JSON.stringify(query); 370 | var ret = false; 371 | 372 | repositoryCache && repositoryCache.some(function(bind) { 373 | var bindDataFormater = bind['dataFormater'].toString(); 374 | var bindQuery = JSON.stringify(bind['query']); 375 | 376 | return (bindQuery === tmpQuery && bindDataFormater === tmpDataFormater && (ret = bind)); 377 | }); 378 | 379 | return ((ret === false) ? false : ret['value']); 380 | }; 381 | 382 | self.putCache = function(repositoryName, query, dataFormater, value) { 383 | if (!repositoryName || !query || !dataFormater || !value) { 384 | return (0); 385 | } 386 | _cache[repositoryName] = (_cache[repositoryName]) ? _cache[repositoryName] : []; 387 | 388 | _cache[repositoryName].push({ 389 | 'query': query, 390 | 'value': value, 391 | 'dataFormater': dataFormater 392 | }); 393 | }; 394 | 395 | self.updateCache = function(repositoryName) { 396 | var repositoryCache = _cache[repositoryName]; 397 | 398 | repositoryCache && repositoryCache.forEach(function(bind) { 399 | var query = ngdbQuery.make(bind['query']['query'], bind['query']['binds']); 400 | 401 | query.then(function(result) { 402 | var newValue = bind['dataFormater'].call(null, result); 403 | var oldValue = angular.copy(bind['value']); 404 | 405 | if (!angular.equals(newValue, oldValue)) { 406 | _mergeData(bind['value'], newValue); 407 | self.callWatcher(bind['value'], oldValue); 408 | } 409 | }); 410 | }); 411 | }; 412 | 413 | /* 414 | ** WATCH METHODS 415 | */ 416 | self.putWatcher = function(value, callback) { 417 | var watcher = _getWatcher(value); 418 | this.watcherId = this.watcherId || 0; 419 | 420 | if (watcher) { 421 | watcher['callbacks'].push({ 422 | 'id': ++this.watcherId, 423 | 'callback': callback 424 | }); 425 | 426 | return (this.watcherId); 427 | } 428 | else { 429 | _watchers.push({ 430 | 'value': value, 431 | 'callbacks': [] 432 | }); 433 | 434 | return (self.putWatcher(value, callback)); 435 | } 436 | 437 | return (0); 438 | }; 439 | 440 | self.popWatcher = function(watcherId) { 441 | var ret = false; 442 | 443 | _watchers.some(function(watcher, index) { 444 | var tmp = watcher['callbacks'].some(function(callback, index) { 445 | return (callback['id'] === watcherId && (ret = index)); 446 | }); 447 | 448 | return (tmp && delete _watchers[index]['callbacks'][ret]); 449 | }); 450 | 451 | return (!(ret === false)); 452 | }; 453 | 454 | self.callWatcher = function(newValue, oldValue) { 455 | var watcher = _getWatcher(newValue); 456 | 457 | if (watcher) { 458 | watcher['callbacks'].forEach(function(callback) { 459 | callback['callback'](newValue, oldValue); 460 | }); 461 | } 462 | }; 463 | 464 | return (self); 465 | } 466 | angular 467 | .module('ngDatabase') 468 | .factory('ngdbDataConverter', ngdbDataConverter); 469 | 470 | ngdbDataConverter.$inject = ['ngdbUtils']; 471 | function ngdbDataConverter(ngdbUtils) { 472 | var self = this; 473 | 474 | /* 475 | ** PRIVATE METHODS 476 | */ 477 | var _isJson = function(val) { 478 | var ret = null; 479 | 480 | try { 481 | ret = angular.fromJson(val); 482 | } catch(e) { 483 | return (false); 484 | } 485 | 486 | return (ret); 487 | }; 488 | 489 | var _convertObjectToAdd = function(val) { 490 | return (angular.isObject(val) && Object.keys(val).length && angular.toJson(val) || undefined); 491 | }; 492 | var _convertArrayToAdd = function(val) { 493 | return (angular.isObject(val) && val.length && angular.toJson(val) || undefined); 494 | }; 495 | var _convertObjectToGet = function(val) { 496 | return (_isJson(val) || undefined); 497 | }; 498 | 499 | var _convertDateToAdd = function(val) { 500 | return (val instanceof Date && val.getTime() || undefined); 501 | }; 502 | var _convertDateToGet = function(val) { 503 | return (isFinite(val) && new Date(val) || undefined); 504 | }; 505 | 506 | var _convertNumberToAdd = function(val) { 507 | return (isFinite(val) && parseInt(val, 10) || undefined); 508 | }; 509 | var _convertNumberToGet = function(val) { 510 | return (isFinite(val) && parseInt(val, 10) || undefined); 511 | }; 512 | 513 | var _convertBoolToAdd = function(val) { 514 | return ((val === true || val === false) ? val.toString() : undefined); 515 | }; 516 | var _convertBoolToGet = function(val) { 517 | return ((val === "true") ? true : false); 518 | }; 519 | 520 | var _convertDataToAdd = function(data, dataType) { 521 | var converter = { 522 | 'OBJECT': _convertObjectToAdd, 523 | 'ARRAY': _convertArrayToAdd, 524 | 'DATE': _convertDateToAdd, 525 | 'BOOLEAN': _convertBoolToAdd, 526 | 'NUMBER': _convertNumberToAdd 527 | }; 528 | 529 | return ((converter[dataType]) ? converter[dataType].call(null, data) : data); 530 | }; 531 | var _convertDataToGet = function(data, dataType) { 532 | var converter = { 533 | 'OBJECT': _convertObjectToGet, 534 | 'ARRAY': _convertObjectToGet, 535 | 'DATE': _convertDateToGet, 536 | 'BOOLEAN': _convertBoolToGet, 537 | 'NUMBER': _convertNumberToGet 538 | }; 539 | 540 | return ((converter[dataType]) ? converter[dataType].call(null, data) : data); 541 | }; 542 | var _convertData = function(data, repositorySchema, fun) { 543 | var formated = (data) ? {} : null; 544 | 545 | ngdbUtils.browseObject(data, function(fieldValue, fieldName) { 546 | if (repositorySchema && repositorySchema[fieldName]) { 547 | var ret = fun(fieldValue, repositorySchema[fieldName]); 548 | 549 | if (ret !== undefined) { 550 | formated[fieldName] = ret; 551 | } 552 | } 553 | }); 554 | 555 | return (formated); 556 | }; 557 | 558 | /* 559 | ** PUBLIC METHODS 560 | */ 561 | self.convertDataToAdd = function(data, repositorySchema) { 562 | return (_convertData(data, repositorySchema, _convertDataToAdd)); 563 | }; 564 | 565 | self.convertDataToGet = function(data, repositorySchema) { 566 | return (_convertData(data, repositorySchema, _convertDataToGet)); 567 | }; 568 | 569 | return (self); 570 | } 571 | angular 572 | .module('ngDatabase') 573 | .service('ngdbQueryBuilder', ngdbQueryBuilder); 574 | 575 | ngdbQueryBuilder.$inject = ['ngdbUtils']; 576 | function ngdbQueryBuilder(ngdbUtils) { 577 | var self = this; 578 | /* PRIVATE ATTRIBUTS */ 579 | var _queryParams = { 580 | 'data': {'matching': [], 'binds': []}, 581 | 'where': {'matching': [], 'binds': []}, 582 | 'order': {'matching': [], 'binds': []}, 583 | 'limit': {'matching': []}, 584 | 'table': null 585 | }; 586 | 587 | /* 588 | ** BUILD QUERY METHODS 589 | */ 590 | var _buildSelectQuery = function() { 591 | return ("SELECT * FROM `" + _queryParams['table']+ "`"); 592 | }; 593 | 594 | var _buildUpdateQuery = function() { 595 | var matching = _queryParams['data']['matching'].map(function(val) { 596 | return ("`" + val + "` = ?"); 597 | }); 598 | 599 | return ("UPDATE `" + _queryParams['table'] + "` SET " + matching.join(",")); 600 | }; 601 | 602 | var _buildInsertQuery = function() { 603 | var matching = _queryParams['data']['matching'].map(function(val) { 604 | return ("?"); 605 | }); 606 | 607 | return ("INSERT INTO `" + _queryParams['table'] + "` (`" + _queryParams['data']['matching'].join("`, `") + "`) VALUES (" + matching.join(",") + ")"); 608 | }; 609 | 610 | var _buildDeleteQuery = function() { 611 | return ("DELETE FROM `" + _queryParams['table'] + "`"); 612 | }; 613 | 614 | /* 615 | ** BUILD PARAMS METHODS 616 | */ 617 | var _buildWhereParam = function() { 618 | var matching = _queryParams['where']['matching'].map(function(val) { 619 | return ("`" + val + "` = ?"); 620 | }); 621 | 622 | return ("WHERE " + matching.join(" and ")); 623 | }; 624 | 625 | var _buildOrderParam = function() { 626 | return ("ORDER BY " + _queryParams['order']['matching'].join(",")); 627 | }; 628 | 629 | var _buildLimitParam = function() { 630 | return ("LIMIT " + _queryParams['limit']['matching'][0] + "," + _queryParams['limit']['matching'][1]); 631 | }; 632 | 633 | var _buildQueryParams = function() { 634 | var subParams = []; 635 | var paramsTemplate = { 636 | "where": _buildWhereParam, 637 | "order": _buildOrderParam, 638 | "limit": _buildLimitParam 639 | }; 640 | 641 | ngdbUtils.browseObject(_queryParams, function(val, key) { 642 | if (val['matching'] && val['matching'].length && key !== "data") { 643 | subParams.push(paramsTemplate[key].call()); 644 | } 645 | }); 646 | 647 | return (subParams.join(" ")); 648 | }; 649 | 650 | /* 651 | ** PROTECTED METHODS 652 | */ 653 | self.ngdbQueryBuilderSetRepository = function(repositoryName) { 654 | _queryParams['table'] = repositoryName; 655 | 656 | return (this); 657 | }; 658 | 659 | self.setData = function(data) { 660 | ngdbUtils.browseObject(data, function(val, key) { 661 | _queryParams['data']['matching'].push(key); 662 | _queryParams['data']['binds'].push(val); 663 | }); 664 | }; 665 | 666 | self.buildQuery = function(queryType, data) { 667 | var queryTemplate = { 668 | 'SELECT': _buildSelectQuery, 669 | 'UPDATE': _buildUpdateQuery, 670 | 'INSERT': _buildInsertQuery, 671 | 'DELETE': _buildDeleteQuery 672 | }; 673 | 674 | self.setData(data); 675 | var query = queryTemplate[queryType].call() + " " + _buildQueryParams(); 676 | var queryBinds = []; 677 | 678 | ngdbUtils.browseObject(_queryParams, function(val) { 679 | if (val['binds'] && val['binds'].length) { 680 | queryBinds = queryBinds.concat(val['binds']); 681 | } 682 | }); 683 | 684 | return ({'query': query, 'binds': queryBinds}); 685 | }; 686 | 687 | self.resetBuilder = function() { 688 | _queryParams = { 689 | 'data': {'matching': [], 'binds': []}, 690 | 'where': {'matching': [], 'binds': []}, 691 | 'order': {'matching': [], 'binds': []}, 692 | 'limit': {'matching': []}, 693 | 'table': _queryParams['table'] 694 | }; 695 | }; 696 | 697 | /* 698 | ** SETTERS 699 | */ 700 | self.setBy = function(where) { 701 | ngdbUtils.browseObject(where, function(val, key) { 702 | _queryParams['where']['matching'].push(key); 703 | _queryParams['where']['binds'].push(val); 704 | }); 705 | 706 | return (this); 707 | }; 708 | 709 | self.setOrder = function(order) { 710 | ngdbUtils.browseObject(order, function(val, key) { 711 | _queryParams['order']['matching'].push(key + " " + val); 712 | }); 713 | 714 | return (this); 715 | }; 716 | 717 | self.setLimit = function(from, to) { 718 | _queryParams['limit']['matching'][0] = parseInt(from, 10); 719 | _queryParams['limit']['matching'][1] = parseInt(to, 10); 720 | 721 | return (this); 722 | }; 723 | 724 | return (self); 725 | } 726 | angular 727 | .module('ngDatabase') 728 | .factory('ngdbUtils', ngdbUtils); 729 | 730 | ngdbUtils.$inject = []; 731 | function ngdbUtils() { 732 | var self = this; 733 | 734 | self.browseObject = function(obj, callback) { 735 | for (var key in obj) { 736 | var val = obj[key]; 737 | 738 | if (val !== undefined && val !== null) { 739 | callback(val, key); 740 | } 741 | } 742 | }; 743 | 744 | self.errorHandler = function(message) { 745 | throw(new Error("NGDB : " + message, "", "")); 746 | }; 747 | 748 | return (self); 749 | } -------------------------------------------------------------------------------- /dist/ngdb.min.js: -------------------------------------------------------------------------------- 1 | function ngdbProvider(n){var t=this;t.repositoriesSchema={};var e=function(t){var e=!0;return ngdbUtils().browseObject(t,function(t){e=n[t]?e:!1}),e};return t.setRepository=function(n,r){return e(r)?(r.id="ID",t.repositoriesSchema[n]=r):ngdbUtils().errorHandler("Unable to create '"+n+"' due to unknown datatype."),t},t.$get=ngdbFactory,t}function ngdbFactory(n,t,e,r,i,a){var u=this,o={},c=function(n){var t={};return e.browseObject(n,function(n,e){t[e]="id"!==e?a[n]:"integer primary key"}),t};return o.createRepositories=function(){var t=[],i=u.repositoriesSchema;return e.browseObject(i,function(n,i){var a=[];n=c(n),e.browseObject(n,function(n,t){a.push("`"+t+"` "+n)}),t.push(r.make("CREATE TABLE IF NOT EXISTS `"+i+"` ("+a.join(", ")+")"))}),n.all(t)},o.getRepository=function(n,e){var r=t.instantiate(ngdbRepository,{ngdbQueryBuilder:t.instantiate(ngdbQueryBuilder)}),i=o.getRepositorySchema(n);return r.ngdbRepositorySetRepository(n,i,e),r},o.getRepositorySchema=function(n){return u.repositoriesSchema[n]||null},o.getQueryMaker=function(){return r},o.putWatcher=function(n,t,e){var r=i.putWatcher(n,t);return(e===!0||"undefined"==typeof e)&&i.callWatcher(n,n),r},o.popWatcher=function(n){return i.popWatcher(n)},o.createRepositories(),o}function ngdbQuery(n,t){var e=this,r=null,i=function(){return r=window.cordova?t.openDB("ngdb.db"):window.openDatabase("ngdb.db","1","ngdb.db",-1)};return e.make=function(t,e){var i=n.defer();return e=void 0!==e&&null!==e?e:[],r.transaction(function(n){n.executeSql(t,e,function(n,t){i.resolve(t)},function(n,t){i.reject(t)})}),i.promise},e.fetchAll=function(n){for(var t=[],e=n.rows.length,r=0;e>r;r++)t.push(n.rows.item(r));return t},e.fetch=function(n){return n.rows.length>0?n.rows.item(0):null},i(),e}function ngdbRepository(n,t,e,r,i,a,u){var o=this,c=!0,d=null,s=null;o.ngdbRepositorySetRepository=function(n,t,e){return d=n,s=t,c=e===!1?!1:!0,i.ngdbQueryBuilderSetRepository(n),o};var l=function(n){var t=r.fetchAll(n);return t&&t.forEach(function(n,e){t[e]=u.convertDataToGet(n,s)}),t},b=function(n){var t=u.convertDataToGet(r.fetch(n),s);return t?t:null},f=function(n){return c?void n.then(function(){a.updateCache(d)}):0};return o.get=function(){var t=n.defer(),e=this.buildQuery("SELECT"),i=a.getCache(d,e,l);if(i===!1){var u=r.make(e.query,e.binds);u.then(function(n){var r=l(n);t.resolve(r),a.putCache(d,e,l,r)},t.reject)}else t.resolve(i);return this.resetBuilder(),t.promise},o.getOne=function(){var t=n.defer(),e=this.setLimit(0,1).buildQuery("SELECT"),i=a.getCache(d,e,b);if(i===!1){var u=r.make(e.query,e.binds);u.then(function(n){var r=b(n);t.resolve(r),a.putCache(d,e,b,r)},t.reject)}else t.resolve(i);return this.resetBuilder(),t.promise},o.add=function(n){n=u.convertDataToAdd(n,s);var t=this.buildQuery("INSERT",n),e=r.make(t.query,t.binds);return f(e),this.resetBuilder(),e},o.update=function(n){n=u.convertDataToAdd(n,s);var t=this.buildQuery("UPDATE",n),e=r.make(t.query,t.binds);return f(e),this.resetBuilder(),e},o.delete=function(){var n=this.buildQuery("DELETE"),t=r.make(n.query,n.binds);return f(t),this.resetBuilder(),t},angular.extend(o,i),o}function ngdbCache(n,t){var e=this,r={},i=[],a=function(n,t){return t&&t.forEach(function(e,r){n[r]=t[r]}),n&&n.forEach(function(e,r){t[r]||n.pop()}),n},u=function(n,e){return e&&t.browseObject(e,function(t,e){n[e]=t}),n&&t.browseObject(n,function(t,r){e&&e[r]||delete n[r]}),n},o=function(n,t){return t instanceof Array?a(n,t):t instanceof Object?u(n,t):u(n,t)},c=function(n){var t=!1;return i.some(function(e){return e.value===n&&(t=e)}),t===!1?!1:t};return e.getCache=function(n,t,e){var i=r[n],a=e.toString(),u=JSON.stringify(t),o=!1;return i&&i.some(function(n){var t=n.dataFormater.toString(),e=JSON.stringify(n.query);return e===u&&t===a&&(o=n)}),o===!1?!1:o.value},e.putCache=function(n,t,e,i){return n&&t&&e&&i?(r[n]=r[n]?r[n]:[],void r[n].push({query:t,value:i,dataFormater:e})):0},e.updateCache=function(t){var i=r[t];i&&i.forEach(function(t){var r=n.make(t.query.query,t.query.binds);r.then(function(n){var r=t.dataFormater.call(null,n),i=angular.copy(t.value);angular.equals(r,i)||(o(t.value,r),e.callWatcher(t.value,i))})})},e.putWatcher=function(n,t){var r=c(n);return this.watcherId=this.watcherId||0,r?(r.callbacks.push({id:++this.watcherId,callback:t}),this.watcherId):(i.push({value:n,callbacks:[]}),e.putWatcher(n,t))},e.popWatcher=function(n){var t=!1;return i.some(function(e,r){var a=e.callbacks.some(function(e,r){return e.id===n&&(t=r)});return a&&delete i[r].callbacks[t]}),!(t===!1)},e.callWatcher=function(n,t){var e=c(n);e&&e.callbacks.forEach(function(e){e.callback(n,t)})},e}function ngdbDataConverter(n){var t=this,e=function(n){var t=null;try{t=angular.fromJson(n)}catch(e){return!1}return t},r=function(n){return angular.isObject(n)&&Object.keys(n).length&&angular.toJson(n)||void 0},i=function(n){return angular.isObject(n)&&n.length&&angular.toJson(n)||void 0},a=function(n){return e(n)||void 0},u=function(n){return n instanceof Date&&n.getTime()||void 0},o=function(n){return isFinite(n)&&new Date(n)||void 0},c=function(n){return isFinite(n)&&parseInt(n,10)||void 0},d=function(n){return isFinite(n)&&parseInt(n,10)||void 0},s=function(n){return n===!0||n===!1?n.toString():void 0},l=function(n){return"true"===n?!0:!1},b=function(n,t){var e={OBJECT:r,ARRAY:i,DATE:u,BOOLEAN:s,NUMBER:c};return e[t]?e[t].call(null,n):n},f=function(n,t){var e={OBJECT:a,ARRAY:a,DATE:o,BOOLEAN:l,NUMBER:d};return e[t]?e[t].call(null,n):n},g=function(t,e,r){var i=t?{}:null;return n.browseObject(t,function(n,t){if(e&&e[t]){var a=r(n,e[t]);void 0!==a&&(i[t]=a)}}),i};return t.convertDataToAdd=function(n,t){return g(n,t,b)},t.convertDataToGet=function(n,t){return g(n,t,f)},t}function ngdbQueryBuilder(n){var t=this,e={data:{matching:[],binds:[]},where:{matching:[],binds:[]},order:{matching:[],binds:[]},limit:{matching:[]},table:null},r=function(){return"SELECT * FROM `"+e.table+"`"},i=function(){var n=e.data.matching.map(function(n){return"`"+n+"` = ?"});return"UPDATE `"+e.table+"` SET "+n.join(",")},a=function(){var n=e.data.matching.map(function(){return"?"});return"INSERT INTO `"+e.table+"` (`"+e.data.matching.join("`, `")+"`) VALUES ("+n.join(",")+")"},u=function(){return"DELETE FROM `"+e.table+"`"},o=function(){var n=e.where.matching.map(function(n){return"`"+n+"` = ?"});return"WHERE "+n.join(" and ")},c=function(){return"ORDER BY "+e.order.matching.join(",")},d=function(){return"LIMIT "+e.limit.matching[0]+","+e.limit.matching[1]},s=function(){var t=[],r={where:o,order:c,limit:d};return n.browseObject(e,function(n,e){n.matching&&n.matching.length&&"data"!==e&&t.push(r[e].call())}),t.join(" ")};return t.ngdbQueryBuilderSetRepository=function(n){return e.table=n,this},t.setData=function(t){n.browseObject(t,function(n,t){e.data.matching.push(t),e.data.binds.push(n)})},t.buildQuery=function(o,c){var d={SELECT:r,UPDATE:i,INSERT:a,DELETE:u};t.setData(c);var l=d[o].call()+" "+s(),b=[];return n.browseObject(e,function(n){n.binds&&n.binds.length&&(b=b.concat(n.binds))}),{query:l,binds:b}},t.resetBuilder=function(){e={data:{matching:[],binds:[]},where:{matching:[],binds:[]},order:{matching:[],binds:[]},limit:{matching:[]},table:e.table}},t.setBy=function(t){return n.browseObject(t,function(n,t){e.where.matching.push(t),e.where.binds.push(n)}),this},t.setOrder=function(t){return n.browseObject(t,function(n,t){e.order.matching.push(t+" "+n)}),this},t.setLimit=function(n,t){return e.limit.matching[0]=parseInt(n,10),e.limit.matching[1]=parseInt(t,10),this},t}function ngdbUtils(){var n=this;return n.browseObject=function(n,t){for(var e in n){var r=n[e];void 0!==r&&null!==r&&t(r,e)}},n.errorHandler=function(n){throw new Error("NGDB : "+n,"","")},n}angular.module("ngDatabase",["ngCordova"]).constant("NGDB_TYPES",{ID:"integer",STRING:"text",NUMBER:"integer",BOOLEAN:"text",OBJECT:"text",ARRAY:"text",DATE:"datetime"}),angular.module("ngDatabase").provider("ngdb",ngdbProvider),ngdbProvider.$inject=["NGDB_TYPES"],ngdbFactory.$inject=["$q","$injector","ngdbUtils","ngdbQuery","ngdbCache","NGDB_TYPES"],angular.module("ngDatabase").factory("ngdbQuery",ngdbQuery),ngdbQuery.$inject=["$q","$cordovaSQLite"],angular.module("ngDatabase").service("ngdbRepository",ngdbRepository),ngdbRepository.$inject=["$q","$injector","ngdbUtils","ngdbQuery","ngdbQueryBuilder","ngdbCache","ngdbDataConverter"],angular.module("ngDatabase").factory("ngdbCache",ngdbCache),ngdbCache.$inject=["ngdbQuery","ngdbUtils"],angular.module("ngDatabase").factory("ngdbDataConverter",ngdbDataConverter),ngdbDataConverter.$inject=["ngdbUtils"],angular.module("ngDatabase").service("ngdbQueryBuilder",ngdbQueryBuilder),ngdbQueryBuilder.$inject=["ngdbUtils"],angular.module("ngDatabase").factory("ngdbUtils",ngdbUtils),ngdbUtils.$inject=[]; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-database", 3 | "version": "1.3.5", 4 | "description": "ngDatabase is a very simple and powerful local storage solution for your Ionic hybrid apps.", 5 | "main": "ngdb.min.js", 6 | "keywords": [ 7 | "ionic", 8 | "storage", 9 | "sqlite" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/abellion/ngDatabase.git" 14 | }, 15 | "author": "Antoine Bellion (http://antoinebellion.com)", 16 | "license": "ISC" 17 | } 18 | -------------------------------------------------------------------------------- /src/ngdb.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('ngDatabase', ['ngCordova']) 3 | .constant('NGDB_TYPES', { 4 | ID: 'integer', 5 | STRING: 'text', 6 | NUMBER: 'integer', 7 | BOOLEAN: 'text', 8 | OBJECT: 'text', 9 | ARRAY: 'text', 10 | DATE: 'datetime' 11 | }); 12 | 13 | angular 14 | .module('ngDatabase') 15 | .provider('ngdb', ngdbProvider); 16 | 17 | ngdbProvider.$inject = ['NGDB_TYPES']; 18 | function ngdbProvider(NGDB_TYPES) { 19 | var self = this; 20 | self.repositoriesSchema = {}; 21 | 22 | var _validRepository = function(repositorySchema) { 23 | var isValid = true; 24 | 25 | ngdbUtils().browseObject(repositorySchema, function(type, name) { 26 | isValid = (NGDB_TYPES[type]) ? isValid : false; 27 | }); 28 | 29 | return (isValid) 30 | }; 31 | 32 | self.setRepository = function(repositoryName, repositorySchema) { 33 | if (_validRepository(repositorySchema)) { 34 | repositorySchema['id'] = 'ID'; 35 | 36 | self.repositoriesSchema[repositoryName] = repositorySchema; 37 | } 38 | else { 39 | ngdbUtils().errorHandler("Unable to create '"+repositoryName+"' due to unknown datatype."); 40 | } 41 | 42 | return (self); 43 | }; 44 | 45 | self.$get = ngdbFactory; 46 | 47 | return (self); 48 | } 49 | 50 | ngdbFactory.$inject = ['$q', '$injector', 'ngdbUtils', 'ngdbQuery', 'ngdbCache', 'NGDB_TYPES']; 51 | function ngdbFactory($q, $injector, ngdbUtils, ngdbQuery, ngdbCache, NGDB_TYPES) { 52 | var self = this; 53 | var ngdb = {}; 54 | 55 | /* 56 | ** REPOSITORIES 57 | */ 58 | var _formatRepository = function(repositorySchema) { 59 | var ret = {}; 60 | 61 | ngdbUtils.browseObject(repositorySchema, function(columnType, columnName) { 62 | ret[columnName] = (columnName !== "id") ? NGDB_TYPES[columnType] : 'integer primary key'; 63 | }); 64 | 65 | return (ret); 66 | }; 67 | 68 | ngdb.createRepositories = function() { 69 | var queries = []; 70 | var schema = self.repositoriesSchema; 71 | 72 | ngdbUtils.browseObject(schema, function(table, tableName) { 73 | var columns = []; 74 | table = _formatRepository(table); 75 | 76 | ngdbUtils.browseObject(table, function(columnType, columnName) { 77 | columns.push('`' + columnName + '` ' + columnType); 78 | }); 79 | 80 | queries.push(ngdbQuery.make('CREATE TABLE IF NOT EXISTS `' + tableName + '` (' + columns.join(', ') + ')')); 81 | }); 82 | 83 | return ($q.all(queries)); 84 | }; 85 | 86 | ngdb.getRepository = function(repositoryName, binding) { 87 | var repository = $injector.instantiate(ngdbRepository, { 'ngdbQueryBuilder': $injector.instantiate(ngdbQueryBuilder) }); 88 | var repositorySchema = ngdb.getRepositorySchema(repositoryName); 89 | 90 | repository.ngdbRepositorySetRepository(repositoryName, repositorySchema, binding); 91 | 92 | return (repository); 93 | }; 94 | 95 | ngdb.getRepositorySchema = function(repositoryName) { 96 | return (self.repositoriesSchema[repositoryName] || null); 97 | }; 98 | 99 | ngdb.getQueryMaker = function() { 100 | return (ngdbQuery); 101 | }; 102 | 103 | /* 104 | ** WATCHERS 105 | */ 106 | ngdb.putWatcher = function(value, callback, call) { 107 | var watcherId = ngdbCache.putWatcher(value, callback); 108 | 109 | if (call === true || typeof call === "undefined") { 110 | ngdbCache.callWatcher(value, value); 111 | } 112 | 113 | return (watcherId); 114 | }; 115 | 116 | ngdb.popWatcher = function(watcherId) { 117 | return (ngdbCache.popWatcher(watcherId)); 118 | }; 119 | 120 | return (ngdb.createRepositories(), ngdb); 121 | }; -------------------------------------------------------------------------------- /src/ngdbCache.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('ngDatabase') 3 | .factory('ngdbCache', ngdbCache); 4 | 5 | ngdbCache.$inject = ['ngdbQuery', 'ngdbUtils']; 6 | function ngdbCache(ngdbQuery, ngdbUtils) { 7 | var self = this; 8 | var _cache = {}; 9 | var _watchers = []; 10 | 11 | /* 12 | ** CACHE UTILS METHODS 13 | */ 14 | var _mergeArray = function(dst, src) { 15 | src && src.forEach(function(val, key) { 16 | dst[key] = src[key]; 17 | }); 18 | dst && dst.forEach(function(val, key) { 19 | if (!src[key]) { 20 | dst.pop(); 21 | } 22 | }); 23 | 24 | return (dst); 25 | }; 26 | 27 | var _mergeObject = function(dst, src) { 28 | src && ngdbUtils.browseObject(src, function(val, key) { 29 | dst[key] = val; 30 | }); 31 | dst && ngdbUtils.browseObject(dst, function(val, key) { 32 | if (!src || !src[key]) { 33 | delete dst[key]; 34 | } 35 | }); 36 | 37 | return (dst); 38 | }; 39 | 40 | var _mergeData = function(dst, src) { 41 | if (src instanceof Array) { 42 | return (_mergeArray(dst, src)); 43 | } 44 | else if (src instanceof Object) { 45 | return (_mergeObject(dst, src)); 46 | } 47 | 48 | return (_mergeObject(dst, src)); 49 | }; 50 | 51 | /* 52 | ** WATCH UTILS METHODS 53 | */ 54 | var _getWatcher = function(value) { 55 | var ret = false; 56 | 57 | _watchers.some(function(watcher) { 58 | return (watcher['value'] === value && (ret = watcher)); 59 | }); 60 | 61 | return ((ret === false) ? false : ret); 62 | }; 63 | 64 | /* 65 | ** CACHE METHODS 66 | */ 67 | self.getCache = function(repositoryName, query, dataFormater) { 68 | var repositoryCache = _cache[repositoryName]; 69 | var tmpDataFormater = dataFormater.toString(); 70 | var tmpQuery = JSON.stringify(query); 71 | var ret = false; 72 | 73 | repositoryCache && repositoryCache.some(function(bind) { 74 | var bindDataFormater = bind['dataFormater'].toString(); 75 | var bindQuery = JSON.stringify(bind['query']); 76 | 77 | return (bindQuery === tmpQuery && bindDataFormater === tmpDataFormater && (ret = bind)); 78 | }); 79 | 80 | return ((ret === false) ? false : ret['value']); 81 | }; 82 | 83 | self.putCache = function(repositoryName, query, dataFormater, value) { 84 | if (!repositoryName || !query || !dataFormater || !value) { 85 | return (0); 86 | } 87 | _cache[repositoryName] = (_cache[repositoryName]) ? _cache[repositoryName] : []; 88 | 89 | _cache[repositoryName].push({ 90 | 'query': query, 91 | 'value': value, 92 | 'dataFormater': dataFormater 93 | }); 94 | }; 95 | 96 | self.updateCache = function(repositoryName) { 97 | var repositoryCache = _cache[repositoryName]; 98 | 99 | repositoryCache && repositoryCache.forEach(function(bind) { 100 | var query = ngdbQuery.make(bind['query']['query'], bind['query']['binds']); 101 | 102 | query.then(function(result) { 103 | var newValue = bind['dataFormater'].call(null, result); 104 | var oldValue = angular.copy(bind['value']); 105 | 106 | if (!angular.equals(newValue, oldValue)) { 107 | _mergeData(bind['value'], newValue); 108 | self.callWatcher(bind['value'], oldValue); 109 | } 110 | }); 111 | }); 112 | }; 113 | 114 | /* 115 | ** WATCH METHODS 116 | */ 117 | self.putWatcher = function(value, callback) { 118 | var watcher = _getWatcher(value); 119 | this.watcherId = this.watcherId || 0; 120 | 121 | if (watcher) { 122 | watcher['callbacks'].push({ 123 | 'id': ++this.watcherId, 124 | 'callback': callback 125 | }); 126 | 127 | return (this.watcherId); 128 | } 129 | else { 130 | _watchers.push({ 131 | 'value': value, 132 | 'callbacks': [] 133 | }); 134 | 135 | return (self.putWatcher(value, callback)); 136 | } 137 | 138 | return (0); 139 | }; 140 | 141 | self.popWatcher = function(watcherId) { 142 | var ret = false; 143 | 144 | _watchers.some(function(watcher, index) { 145 | var tmp = watcher['callbacks'].some(function(callback, index) { 146 | return (callback['id'] === watcherId && (ret = index)); 147 | }); 148 | 149 | return (tmp && delete _watchers[index]['callbacks'][ret]); 150 | }); 151 | 152 | return (!(ret === false)); 153 | }; 154 | 155 | self.callWatcher = function(newValue, oldValue) { 156 | var watcher = _getWatcher(newValue); 157 | 158 | if (watcher) { 159 | watcher['callbacks'].forEach(function(callback) { 160 | callback['callback'](newValue, oldValue); 161 | }); 162 | } 163 | }; 164 | 165 | return (self); 166 | } -------------------------------------------------------------------------------- /src/ngdbDataConverter.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('ngDatabase') 3 | .factory('ngdbDataConverter', ngdbDataConverter); 4 | 5 | ngdbDataConverter.$inject = ['ngdbUtils']; 6 | function ngdbDataConverter(ngdbUtils) { 7 | var self = this; 8 | 9 | /* 10 | ** PRIVATE METHODS 11 | */ 12 | var _isJson = function(val) { 13 | var ret = null; 14 | 15 | try { 16 | ret = angular.fromJson(val); 17 | } catch(e) { 18 | return (false); 19 | } 20 | 21 | return (ret); 22 | }; 23 | 24 | var _convertObjectToAdd = function(val) { 25 | return (angular.isObject(val) && Object.keys(val).length && angular.toJson(val) || undefined); 26 | }; 27 | var _convertArrayToAdd = function(val) { 28 | return (angular.isObject(val) && val.length && angular.toJson(val) || undefined); 29 | }; 30 | var _convertObjectToGet = function(val) { 31 | return (_isJson(val) || undefined); 32 | }; 33 | 34 | var _convertDateToAdd = function(val) { 35 | return (val instanceof Date && val.getTime() || undefined); 36 | }; 37 | var _convertDateToGet = function(val) { 38 | return (isFinite(val) && new Date(val) || undefined); 39 | }; 40 | 41 | var _convertNumberToAdd = function(val) { 42 | var isNumber = (!isNaN(parseFloat(val)) && isFinite(val) || undefined); 43 | 44 | return ((isNumber) ? parseInt(val, 10) : undefined); 45 | }; 46 | var _convertNumberToGet = function(val) { 47 | var isNumber = (!isNaN(parseFloat(val)) && isFinite(val) || undefined); 48 | 49 | return ((isNumber) ? parseInt(val, 10) : undefined); 50 | }; 51 | 52 | var _convertBoolToAdd = function(val) { 53 | return ((val === true || val === false) ? val.toString() : undefined); 54 | }; 55 | var _convertBoolToGet = function(val) { 56 | return ((val === "true") ? true : false); 57 | }; 58 | 59 | var _convertDataToAdd = function(data, dataType) { 60 | var converter = { 61 | 'OBJECT': _convertObjectToAdd, 62 | 'ARRAY': _convertArrayToAdd, 63 | 'DATE': _convertDateToAdd, 64 | 'BOOLEAN': _convertBoolToAdd, 65 | 'NUMBER': _convertNumberToAdd 66 | }; 67 | 68 | return ((converter[dataType]) ? converter[dataType].call(null, data) : data); 69 | }; 70 | var _convertDataToGet = function(data, dataType) { 71 | var converter = { 72 | 'OBJECT': _convertObjectToGet, 73 | 'ARRAY': _convertObjectToGet, 74 | 'DATE': _convertDateToGet, 75 | 'BOOLEAN': _convertBoolToGet, 76 | 'NUMBER': _convertNumberToGet 77 | }; 78 | 79 | return ((converter[dataType]) ? converter[dataType].call(null, data) : data); 80 | }; 81 | var _convertData = function(data, repositorySchema, fun) { 82 | var formated = (data) ? {} : null; 83 | 84 | ngdbUtils.browseObject(data, function(fieldValue, fieldName) { 85 | if (repositorySchema && repositorySchema[fieldName]) { 86 | var ret = fun(fieldValue, repositorySchema[fieldName]); 87 | 88 | if (ret !== undefined) { 89 | formated[fieldName] = ret; 90 | } 91 | } 92 | }); 93 | 94 | return (formated); 95 | }; 96 | 97 | /* 98 | ** PUBLIC METHODS 99 | */ 100 | self.convertDataToAdd = function(data, repositorySchema) { 101 | return (_convertData(data, repositorySchema, _convertDataToAdd)); 102 | }; 103 | 104 | self.convertDataToGet = function(data, repositorySchema) { 105 | return (_convertData(data, repositorySchema, _convertDataToGet)); 106 | }; 107 | 108 | return (self); 109 | } 110 | -------------------------------------------------------------------------------- /src/ngdbQuery.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('ngDatabase') 3 | .factory('ngdbQuery', ngdbQuery); 4 | 5 | ngdbQuery.$inject = ['$q', '$cordovaSQLite']; 6 | function ngdbQuery($q, $cordovaSQLite) { 7 | var self = this; 8 | var _db = null; 9 | 10 | var _dbConnexion = function() { 11 | 12 | _db = (window.cordova) ? 13 | $cordovaSQLite.openDB('ngdb.db') : 14 | window.openDatabase('ngdb.db', '1', 'ngdb.db', -1); 15 | 16 | return (_db); 17 | }; 18 | 19 | 20 | self.make = function(query, bindings) { 21 | var deferred = $q.defer(); 22 | bindings = (bindings !== undefined && bindings !== null) ? bindings : []; 23 | 24 | _db.transaction(function(transaction) { 25 | transaction.executeSql(query, bindings, function(transaction, result) { 26 | deferred.resolve(result); 27 | }, function(transaction, error) { 28 | deferred.reject(error); 29 | }); 30 | }); 31 | 32 | return (deferred.promise); 33 | }; 34 | 35 | self.fetchAll = function(result) { 36 | var output = []; 37 | var rows = result.rows.length; 38 | 39 | for (var i = 0; i < rows; i++) { 40 | output.push(result.rows.item(i)); 41 | } 42 | 43 | return (output); 44 | }; 45 | 46 | self.fetch = function(result) { 47 | return ((result.rows.length > 0) ? result.rows.item(0) : null); 48 | }; 49 | 50 | return (_dbConnexion(), self); 51 | } -------------------------------------------------------------------------------- /src/ngdbQueryBuilder.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('ngDatabase') 3 | .service('ngdbQueryBuilder', ngdbQueryBuilder); 4 | 5 | ngdbQueryBuilder.$inject = ['ngdbUtils']; 6 | function ngdbQueryBuilder(ngdbUtils) { 7 | var self = this; 8 | /* PRIVATE ATTRIBUTS */ 9 | var _queryParams = { 10 | 'data': {'matching': [], 'binds': []}, 11 | 'where': {'matching': [], 'binds': []}, 12 | 'order': {'matching': [], 'binds': []}, 13 | 'limit': {'matching': []}, 14 | 'table': null 15 | }; 16 | 17 | /* 18 | ** BUILD QUERY METHODS 19 | */ 20 | var _buildSelectQuery = function() { 21 | return ("SELECT * FROM `" + _queryParams['table']+ "`"); 22 | }; 23 | 24 | var _buildUpdateQuery = function() { 25 | var matching = _queryParams['data']['matching'].map(function(val) { 26 | return ("`" + val + "` = ?"); 27 | }); 28 | 29 | return ("UPDATE `" + _queryParams['table'] + "` SET " + matching.join(",")); 30 | }; 31 | 32 | var _buildInsertQuery = function() { 33 | var matching = _queryParams['data']['matching'].map(function(val) { 34 | return ("?"); 35 | }); 36 | 37 | return ("INSERT INTO `" + _queryParams['table'] + "` (`" + _queryParams['data']['matching'].join("`, `") + "`) VALUES (" + matching.join(",") + ")"); 38 | }; 39 | 40 | var _buildDeleteQuery = function() { 41 | return ("DELETE FROM `" + _queryParams['table'] + "`"); 42 | }; 43 | 44 | /* 45 | ** BUILD PARAMS METHODS 46 | */ 47 | var _buildWhereParam = function() { 48 | var matching = _queryParams['where']['matching'].map(function(val) { 49 | return ("`" + val + "` = ?"); 50 | }); 51 | 52 | return ("WHERE " + matching.join(" and ")); 53 | }; 54 | 55 | var _buildOrderParam = function() { 56 | return ("ORDER BY " + _queryParams['order']['matching'].join(",")); 57 | }; 58 | 59 | var _buildLimitParam = function() { 60 | return ("LIMIT " + _queryParams['limit']['matching'][0] + "," + _queryParams['limit']['matching'][1]); 61 | }; 62 | 63 | var _buildQueryParams = function() { 64 | var subParams = []; 65 | var paramsTemplate = { 66 | "where": _buildWhereParam, 67 | "order": _buildOrderParam, 68 | "limit": _buildLimitParam 69 | }; 70 | 71 | ngdbUtils.browseObject(_queryParams, function(val, key) { 72 | if (val['matching'] && val['matching'].length && key !== "data") { 73 | subParams.push(paramsTemplate[key].call()); 74 | } 75 | }); 76 | 77 | return (subParams.join(" ")); 78 | }; 79 | 80 | /* 81 | ** PROTECTED METHODS 82 | */ 83 | self.ngdbQueryBuilderSetRepository = function(repositoryName) { 84 | _queryParams['table'] = repositoryName; 85 | 86 | return (this); 87 | }; 88 | 89 | self.setData = function(data) { 90 | ngdbUtils.browseObject(data, function(val, key) { 91 | _queryParams['data']['matching'].push(key); 92 | _queryParams['data']['binds'].push(val); 93 | }); 94 | }; 95 | 96 | self.buildQuery = function(queryType, data) { 97 | var queryTemplate = { 98 | 'SELECT': _buildSelectQuery, 99 | 'UPDATE': _buildUpdateQuery, 100 | 'INSERT': _buildInsertQuery, 101 | 'DELETE': _buildDeleteQuery 102 | }; 103 | 104 | self.setData(data); 105 | var query = queryTemplate[queryType].call() + " " + _buildQueryParams(); 106 | var queryBinds = []; 107 | 108 | ngdbUtils.browseObject(_queryParams, function(val) { 109 | if (val['binds'] && val['binds'].length) { 110 | queryBinds = queryBinds.concat(val['binds']); 111 | } 112 | }); 113 | 114 | return ({'query': query, 'binds': queryBinds}); 115 | }; 116 | 117 | self.resetBuilder = function() { 118 | _queryParams = { 119 | 'data': {'matching': [], 'binds': []}, 120 | 'where': {'matching': [], 'binds': []}, 121 | 'order': {'matching': [], 'binds': []}, 122 | 'limit': {'matching': []}, 123 | 'table': _queryParams['table'] 124 | }; 125 | }; 126 | 127 | /* 128 | ** SETTERS 129 | */ 130 | self.setBy = function(where) { 131 | ngdbUtils.browseObject(where, function(val, key) { 132 | _queryParams['where']['matching'].push(key); 133 | _queryParams['where']['binds'].push(val); 134 | }); 135 | 136 | return (this); 137 | }; 138 | 139 | self.setOrder = function(order) { 140 | ngdbUtils.browseObject(order, function(val, key) { 141 | _queryParams['order']['matching'].push(key + " " + val); 142 | }); 143 | 144 | return (this); 145 | }; 146 | 147 | self.setLimit = function(from, to) { 148 | _queryParams['limit']['matching'][0] = parseInt(from, 10); 149 | _queryParams['limit']['matching'][1] = parseInt(to, 10); 150 | 151 | return (this); 152 | }; 153 | 154 | return (self); 155 | } -------------------------------------------------------------------------------- /src/ngdbRepository.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('ngDatabase') 3 | .service('ngdbRepository', ngdbRepository); 4 | 5 | ngdbRepository.$inject = ['$q', '$injector', 'ngdbUtils', 'ngdbQuery', 'ngdbQueryBuilder', 'ngdbCache', 'ngdbDataConverter']; 6 | function ngdbRepository($q, $injector, ngdbUtils, ngdbQuery, ngdbQueryBuilder, ngdbCache, ngdbDataConverter) { 7 | var self = this; 8 | var _binding = true; 9 | var _repositoryName = null; 10 | var _repositorySchema = null; 11 | 12 | /* 13 | ** UTILS METHODS 14 | */ 15 | self.ngdbRepositorySetRepository = function(repositoryName, repositorySchema, binding) { 16 | _repositoryName = repositoryName; 17 | _repositorySchema = repositorySchema; 18 | _binding = (binding === false) ? false : true; 19 | 20 | ngdbQueryBuilder.ngdbQueryBuilderSetRepository(repositoryName); 21 | 22 | return (self); 23 | }; 24 | 25 | var _formatGet = function(result) { 26 | var fetched = ngdbQuery.fetchAll(result); 27 | 28 | fetched && fetched.forEach(function(val, index) { 29 | fetched[index] = ngdbDataConverter.convertDataToGet(val, _repositorySchema); 30 | }); 31 | 32 | return (fetched); 33 | }; 34 | 35 | var _formatGetOne = function(result) { 36 | var fetched = ngdbDataConverter.convertDataToGet(ngdbQuery.fetch(result), _repositorySchema); 37 | 38 | return ((fetched) ? fetched : null); 39 | }; 40 | 41 | var _updateCache = function(promise) { 42 | if (!_binding) { 43 | return (0); 44 | } 45 | 46 | promise.then(function() { 47 | ngdbCache.updateCache(_repositoryName); 48 | }); 49 | }; 50 | 51 | /* 52 | ** USER METHODS 53 | */ 54 | self.get = function() { 55 | var deferred = $q.defer(); 56 | var query = this.buildQuery('SELECT'); 57 | var cache = ngdbCache.getCache(_repositoryName, query, _formatGet); 58 | 59 | if (cache === false) { 60 | var result = ngdbQuery.make(query['query'], query['binds']); 61 | 62 | result.then(function(result) { 63 | var formated = _formatGet(result); 64 | 65 | deferred.resolve(formated); 66 | ngdbCache.putCache(_repositoryName, query, _formatGet, formated); 67 | }, deferred.reject); 68 | } 69 | else { 70 | deferred.resolve(cache); 71 | } 72 | 73 | return (this.resetBuilder(), deferred.promise); 74 | }; 75 | 76 | self.getOne = function() { 77 | var deferred = $q.defer(); 78 | var query = this.setLimit(0, 1).buildQuery('SELECT'); 79 | var cache = ngdbCache.getCache(_repositoryName, query, _formatGetOne); 80 | 81 | if (cache === false) { 82 | var result = ngdbQuery.make(query['query'], query['binds']); 83 | 84 | result.then(function(result) { 85 | var formated = _formatGetOne(result); 86 | 87 | deferred.resolve(formated); 88 | ngdbCache.putCache(_repositoryName, query, _formatGetOne, formated); 89 | }, deferred.reject); 90 | } 91 | else { 92 | deferred.resolve(cache); 93 | } 94 | 95 | return (this.resetBuilder(), deferred.promise); 96 | }; 97 | 98 | self.add = function(data) { 99 | data = ngdbDataConverter.convertDataToAdd(data, _repositorySchema); 100 | var query = this.buildQuery('INSERT', data); 101 | var result = ngdbQuery.make(query['query'], query['binds']); 102 | 103 | _updateCache(result); 104 | return (this.resetBuilder(), result); 105 | }; 106 | 107 | self.update = function(data) { 108 | data = ngdbDataConverter.convertDataToAdd(data, _repositorySchema); 109 | var query = this.buildQuery('UPDATE', data); 110 | var result = ngdbQuery.make(query['query'], query['binds']); 111 | 112 | _updateCache(result); 113 | return (this.resetBuilder(), result); 114 | }; 115 | 116 | self.delete = function() { 117 | var query = this.buildQuery('DELETE'); 118 | var result = ngdbQuery.make(query['query'], query['binds']); 119 | 120 | _updateCache(result); 121 | return (this.resetBuilder(), result); 122 | }; 123 | 124 | angular.extend(self, ngdbQueryBuilder); 125 | 126 | return (self); 127 | } -------------------------------------------------------------------------------- /src/ngdbUtils.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('ngDatabase') 3 | .factory('ngdbUtils', ngdbUtils); 4 | 5 | ngdbUtils.$inject = []; 6 | function ngdbUtils() { 7 | var self = this; 8 | 9 | self.browseObject = function(obj, callback) { 10 | for (var key in obj) { 11 | var val = obj[key]; 12 | 13 | if (val !== undefined && val !== null) { 14 | callback(val, key); 15 | } 16 | } 17 | }; 18 | 19 | self.errorHandler = function(message) { 20 | throw(new Error("NGDB : " + message, "", "")); 21 | }; 22 | 23 | return (self); 24 | } --------------------------------------------------------------------------------