bramski / angular-indexeddb Goto Github PK
View Code? Open in Web Editor NEWThis project forked from webcss/angular-indexeddb
An angularjs serviceprovider to utilize indexedDB with angular
This project forked from webcss/angular-indexeddb
An angularjs serviceprovider to utilize indexedDB with angular
In my code I have a function like that:
findOneByIndex: function(entityName, id, index) {
var d = $q.defer();
var promise = d.promise;
$indexedDB.openStore(entityName, function(table) {
table.findWhere(table.query().$index(index).$eq(id)).then(function(result) {
if (result.length === 0) {
console.log('No objects found');
return d.reject(result);
} else {
return d.resolve(result[0]);
}
}, function(error) {
console.log(error);
});
}, 'readwrite').then(null, function(error) {
console.log(error);
});
return promise;
}
If I try to query IDB with an index that does not exist, I would expect the exception gets caught and logged. The problem is when I try to test this behaviour, I get:
Uncaught NotFoundError: Failed to execute 'index' on 'IDBObjectStore': The specified index was not found.
at angular-mocks/angular-mocks.js:9
I was wondering if it's a problem with angular mocks or with angular-indexedDB.
In my test I am injecting $indexedDB
Kindly see this snippet in the README:
`angular.module('myModuleName')
.controller('myControllerName', function($scope, $indexedDB) {
$scope.objects = [];
$indexedDB.openStore('people', function(store){
store.insert({"ssn": "444-444-222-111","name": "John Doe", "age": 57}).then(function(e){...});
store.getAll().then(function(people) {
// Update scope
$scope.objects = people;
});
});
});`
The store.getAll()
should probably come out of that indexedDB.openStore(....) { ... }
block.
When I tried the above snippet, the objects are added on first time, if the db doesn't exist, but subsequent ones, no retrieval happens
I did this and it works fine:
$indexedDB.openStore('myStop', function(store) {
store.getAll().then(function(data) {
// Update scope
$scope.objects = data;
});
});
When installing via bower, as suggested: bower install angular-indexed-db
, I get the files placed in bower_components/angular-indexedDB
. Seems like the <script>
tag should be
<script src="bower_components/angular-indexedDB/angular-indexed-db.js"></script>
then, no?
how to print the error at console panel?
This would be really nice.
When I am online, it's works pretty well. But when the connection get lost and perform any operation I get the above mentioned error I am not able to figure it out how to fix it.
Hi bramski,
I'm opening another issue, because I tryed to develope another way to solve the issue #11, using Restangular, I would fetch my api entity if is not stored inside indexedDB store.
$indexedDB.openStore(entityName, function(table) {
var d = $q.defer();
var promise = d.promise;
table.find(id).then(function(success) { //Change if id is used with index
return d.resolve(success);
}, function(error) {
Restangular.one(entityName, id).get().then(function(result) {
table.insert(result, function(result) {
console.log('insert ok');
});
});
return d.reject(error);
});
return promise;
}, 'readwrite');
I have Error: Failed to execute 'add' on 'IDBObjectStore': The transaction has finished.
I understand the reason for async callback for restangular refers to table after the transaction is closed, is there any way to keep the transaction open and manage it inside restangular callback?
Thanks a lot for your support.
G
I'm trying to figure out how I can filter my data against multiple columns.
The schema for the object being stored in indexeddb is:
"Environment": { type: "string", editable: false, nullable: false },
"SequenceId": { type: "string", editable: false, nullable: false },
"LogLevel": { type: "string", editable: false, nullable: false },
"ServerTime": { type: "date", editable: false, nullable: false },
"Program": { type: "string", editable: false, nullable: false },
"Subject": { type: "string", editable: false, nullable: true },
"Message": { type: "string", editable: false, nullable: true },
"Exception": { type: "string", editable: false, nullable: true },
"User": { type: "string", editable: false, nullable: false },
"MachineName": { type: "string", editable: false, nullable: false },
"InstanceId": { type: "string", editable: false, nullable: false },
"ComponentId": { type: "string", editable: false, nullable: false },
"ScribeId": { type: "string", editable: false, nullable: false },
I have set up and configured my object store as:
$indexedDBProvider.connection("ScribeMessagesDB").
upgradeDatabase(1, function (event, db, tx) {
var objStore = db.createObjectStore("ScribeMessages", {
keyPath: ["Environment", "SequenceId"], unique: true });
objStore.createIndex("program_idx", "Program", { unique: false });
objStore.createIndex("logLevel_idx", "LogLevel", { unique: false });
objStore.createIndex("machineName_idx", "MachineName", { unique: false });
objStore.createIndex("user_idx", "User", { unique: false });
objStore.createIndex("environment", "Environment", { unique: false });
objStore.createIndex("sequenceId", "SequenceId", { unique: false });
});
I am trying to filter this way:
$indexedDB.openStore("ScribeMessages", function(store) {
var query = store.query();
store.findWhere(query.$index("environment").$eq("REN-Prod").$index("sequenceId").$gt(0).$desc(false)).then(function(limitedObjects) {
e.success(limitedObjects);
});
});
I should not be getting any values back, as the "Environment" column in the doesn't contain any values for "REN-Prod".
Also, how do I just query against the primary key?
thank you for your help.
marc
The source is very beautiful , it's will be a popular project after the doc completed.
Hello,
The iOS 8 implementation has a bug in it. When the database is opened for the first time iOS 8 returns an absurd high version number, 9223372036854776000. This prevents the upgrade mechanism from creating the object stores and indexes. I fixed it in the generated JavaScript for our project. Could you include my fix in the CoffeeScript?
Regards, Pieter
dbReq.onupgradeneeded = function(event) {
// Change door GINO, iOS 8 geeft initeel een enorm getal als versie in plaats van 0:
var oldVersion = event.oldVersion;
if (oldVersion == 9223372036854776000) {
oldVersion = 0;
}
var tx;
db = event.target.result;
tx = event.target.transaction;
$log.log("$indexedDB: Upgrading database '" + db.name + "' from version " + oldVersion + " to version " + event.newVersion + " ...");
applyNeededUpgrades(oldVersion, event, db, tx, $log);
};
How can I set the indexeddb to temporary?
Hi Guys!
I tried tu use this library with a Cordova mobile app with Angular but the commands are never executed.
Has anyone used this with Cordova?
Thanks!
Are they any query functions that can be used to support paging, like skip() and take()?
Thanks
app.js
var cursosStore = db.createObjectStore('Cursos',
{keypath: 'id', autoincrement: true}
);
Controller
$indexedDB.openStore('materias', function (store) {
var materia1 = {
id: 1,
nombre: "prueba",
descripcion: "prueba"
};
store.insert()
.then(
function (data) {
console.log(data);
},
function (error) {
console.log(error);
}
);
});
Is it necessary to install bower as part of the preinstall and postinstall NPM scripts?
"preinstall": "bower >/dev/null || sudo npm install bower grunt-cli -g",
"postinstall": "bower install"
Eliminating these would eliminate an unnecesary installation step (and invocation of sudo
) for NPM users.
Hi bramski,
Thanks for your work. I'm new to angularjs and I'm trying to use this useful library in my project. My question is: I know indexeddb use cursor. Can I use it to implement a pagination function with angular-indexedDB library. I didn't found a function to access the cursor.
Best regards
Giuseppe
I'm new to indexedDB and trying to create complex keys as the example below, but it is giving me error
var aStore = db.createObjectStore('aStore', {keyPath: ['a_id', 'b_code']});
a.createIndex('c_idx', {'c' : ['c.d', 'c.f']} , {unique: false});
ERROR
Uncaught SyntaxError: Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument contains an invalid key path.(anonymous function) @ app.js:29applyNeededUpgrades @ angular-indexed-db.js:52dbReq.onupgradeneeded @ angular-indexed-db.js:131
angular.js:10627 $indexedDB: myIndexedDB database deleted.
Examples using angular-indexedDB to create/use complex keys are much appreciated
Thanks!
Unless I'm reading this wrong, I think to insert data during config/upgrade time you need to go through the native objects, transactions, etc. It would be nice to get the same interface that $indexedDB.openStore gives you.
In the example for inserting I see this code:
$indexedDB.openStore('people', (store) ->
store.insert({"ssn": "444-444-222-111","name": "John Doe", "age": 57}).then(function(e){...});
store.getAll().then(function(people) {
// Update scope
$scope.objects = people;
});
I don't know coffee script, but I I've been trying to mke it work in plain js, i converted it to:
$indexedDB.openStore('name', function(store){
store.insert({
'_id': shape._id,
'other': other,
'data': shape
});
});
but it doesn't work, any advice?
Hi
Using the newest IE 11
(as of today) on Windows 11 pro
, I encountered the following wired InvalidAccessError
:
Which happens on this line.
The root cause of this is because MS IndexedDB implementation (for whatever reason not clear to me) expects a fix number literal as a second argument and refuses to evaluate any expression like dbVersion || 1
or dbVersion
etc. The same line works like a charm using Chrome Version 42.0.2311.135 m
on Windows.
My current workaround is:
dbReq = indexedDB.open(dbName, 1);
Which of course is not a real fix to this issue!
I know its not recommended but if there is a good use case for it, what is the least detrimental way of doing it?
upgradeDB function stores the upgrade callback functions with its version number as a key in javascript associative arrays.
But the ordering of key is not guaranteed to be preserved in javascript. (I'm not sure if the ordering is preserved in all browsers although it is not in javascript specification) so there is a possibility to apply modification to db in wrong order.
eg. create object-storeA in version1 and delete object-storeA in version2. (these should not be applied in reverse order)
Hi,
In he original code, console.logs were used for dev purpose. It seems that we can't disable them now.
Hi,
I am unable to use $indexedDB, $indexedDBProvider in any angular service.
They are only accessible in controller and config respectively. Can some one tell me a way around this ?
Hello,
have you tested it on Safari?
I'm running an app successfully on Chrome and FF on Mac, but Safari is not ok, but not giving any error.
Thanks,
Mitch
$indexedDBProvider
.connection('myIndexedDB11')
.upgradeDatabase(1, function(event, db, tx) {
var objStore = db.createObjectStore('5173', {
keyPath: "id",
autoIncrement: true
});
objStore.createIndex('i3n_idx', ['hero','skin','price'], {
unique: false
});
});
$indexedDB.openStore('5173', function(store) {
var find = store.query();
// $between(lower, upper, doNotIncludeLowerBound? true/false, doNotIncludeUpperBound true/false) - between two bounds
find.$between(
[$scope.vm.Hmin, $scope.vm.Smin, $scope.vm.Pmin], [$scope.vm.Hmax, $scope.vm.Smax, $scope.vm.Pmax], true, true
);
find.$index("i3n_idx");
store.eachWhere(find).then(function(accounts) {
$scope.vm.result = accounts;
});
});
It looks like it just looking at the first condition( Hmin,Hmax ), ignoring the other conditions [(Smin,Smax)...].
The same code in IE seems to take as huge amount of time when calling getAll() as compared to Chrome.
In Chrome this code runs in <1s, in IE, it takes about minute to reach the console line. Is there something I can do to speed this up?
var loadWhiteListNodeIds = function () {
$indexedDB.openStore('whiteListNodeIds',
function (table) {
return table.find(7672990).then(function (item) {
table.getAll().then(function (whitelistNodeIds) {
console.log('setting whiteListNodeIds');
$scope.whitelistNodeIds = whitelistNodeIds;
});
})["catch"](function (error) {
return getAllWhiteListNodesAndAddToDb('whiteListNodeIds');
});
});
};
Usage examples are helpful for figure out how to use this library.
Especially, I like to know how to get the database object for binding onerror event for error handling globally like :
db.onerror = function(event) {
// Generic error handler for all errors targeted at this database's
// requests!
alert("Database error: " + event.target.errorCode);
};
Thank you very much.
Currently bower.json
restricts angular version to 1.2-1.3
, but it seems to be also working with 1.4 (I have done only minimal testing: set up store, and do an upsert and query data).
Angular migration docs from 1.3-1.4: https://docs.angularjs.org/guide/migration#migrating-from-1-3-to-1-4
Hi bramski
I have a problem using angular-indexedDB module with Restangular.
Failed to execute 'add' on 'IDBObjectStore': An object could not be cloned.
I try to run:
Restangular.one(entityName, id).get().then(function(result) {
$indexedDB.openStore(entityName, function(table) {
table.insert(result, function(result) {
console.log('insert ok');
});
});
});
Is possible to use your module with Restangular or an openStore after a promise result?
Thanks for your help.
Gaspare
Hi Bramski
I'm using your angularjs indexeddb module for iOS support I had to fix putting the following code into
angular-indexed-db.js on line 15
indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
Do you think it could be possible to update your module with this fix, it will help me a lot in managing your library.
Thanks
Gaspare
I found the deleteDatabase
method wasn't deleting my database correctly; it was clearing the stores I had contained within it but not deleting the database itself. The approach used is a little puzzling - why does this service make use of a DbQ
wrapper, and why this functionality is needed over the $q
service that comes with angular? Maybe I'm missing something...
original (not working as expected)
deleteDatabase: function () {
return closeDatabase().then(function () {
var defer;
defer = new DbQ();
defer.resolveWith(indexedDB.deleteDatabase(dbName));
return defer.promise;
})["finally"](function () {
return $log.log("$indexedDB: " + dbName + " database deleted.");
});
}
modified (working)
deleteDatabase: function () {
return closeDatabase().then(function() {
var deferred = $q.defer();
indexedDB.deleteDatabase(dbName).then(function (result) {
console.log("$indexedDB: " + dbName + " database deleted.")
deferred.resolve(result);
}).catch(function(error) {
deferred.reject(error);
});
return defer.promise;
});
}
Is it just outdated? I can submit a pull request if you'd like.
Readme shows how to create a new store on upgrade, but how can I add an index to an existing store?
Currently, the following lines are outside of the angular.module
definition, which means they run immediately when the file is loaded, rather than waiting for angular.bootstrap
to occur.
var IDBKeyRange, indexedDB,
__slice = [].slice;
indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
IDBKeyRange = window.IDBKeyRange || window.mozIDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
The problem is that if I'm running in a browser that doesn't natively support IndexedDB, and I'm loading a polyfill instead, then the above code will only work if the polyfill is loaded before the angular-indexed-db.js
file. But if the polyfill is being loaded dynamically (say, as an Angular module, a Require.js module, a Browserify package, or a Cordova plugin), then the polyfill won't load in time, and Angular-IndexedDB won't work.
To fix this problem, the four lines of code shown above just need to be moved inside of the angular.module
definition. That way, they won't run until all dynamically-loaded scripts are finished loading and angular.bootstrap
is called.
In my case, I have to add stores when needed. But looks like this implementation offers to create stores during config phase. Can you please guide me to create multiple stores dynamically.
Hello, how are you doing?
I started using angular-indexedDB today, but I'm having dificulties working with the store.find() function.
You see, I'm defining a store like this:
db.createObjectStore('campaigns', {keyPath: 'id', autoIncrement: true});
... and I'm able to have insert() and getAll() work as I expected. But, when I try to use the store.find(), like this:
store.find($state.params.campaign_id).then(function(campaign) {
$scope.campaign = campaign;
});
... it simply, doesn't works.
I already have certified (through simple debugging with console.log) that the $state.params are well-formed, and correct, also that there's an entry which possesses a property id equal to the parameter, when the function is called.
Could it be that the store.find() function does not behaves properly when the store have a keyPath and a keyGenerator (autoIncrement) ?
--UPDATE 09/08/2015--
Greetings.
I was able to make it work as I wanted, but I had to pass the entire campaign object through the params, instead of only the id:
store.find($state.params.campaign.id).then(function(campaign) {
$scope.campaign = campaign;
It seems the store.find() function does not performs a deep comparison between the store.key and the value passed as argument, what would explain why passing a by-copy value (instead of the actual reference) does not works.
Also I'm not sure if the store.find() function's key comparison takes data type (primitives, like String, Number, etc) into account.
The project for which this project was written is going EOL and that means that this is highly unlikely to see any active use from me for a very long time. I would be quite happy to keep the project hosted under my username but to add a collaborator to the repo as this codebase is rapidly fading from my memory.
Right now the store incorrectly uses .contains on an array which always returns true.
The current implementation only supports to retrieve values in getAll, each, etc. Please also add methods to retrieve the keys in combination with the values as the keys are necessary for example for deletion, e.g.:
ObjectStore.prototype.getAllKeyValues = function() {
var defer;
defer = this.defer();
if (this.store.getAll) {
defer.resolveWith(this.store.getAll());
} else {
this._mapCursor(defer, function(cursor) {
return {key:cursor.key, value:cursor.value};
});
}
return defer.promise;
};
Just provide a map with "key" and "value" attributes.
Mathias
I got a DataError (code 0) on IE11 Edge when using the eachBy on an index but I did not pass any options. I am calling eachBy without the second parameter, like eachBy("indexName"). The problem is that when the query keyRange is set using:
ObjectStore.prototype.eachBy = function(indexName, options) {
...
q = new Query();
...
q.keyRange = keyRangeForOptions(options);
...
};
The keyRangeForOptions
function does not return anything in the case when options is not specified. This does not appear to affect Chrome or Firefox as I've been using the code for a while. However, I found that IE11 will not work with an undefined keyRange, it must be set to null. So my fix was to change:
keyRangeForOptions = function(options) {
if (options.beginKey && options.endKey) {
return IDBKeyRange.bound(options.beginKey, options.endKey);
}
};
to:
keyRangeForOptions = function(options) {
if (options.beginKey && options.endKey) {
return IDBKeyRange.bound(options.beginKey, options.endKey);
} else {
return null;
}
};
I will work on a Pull Request when I get time. I just wanted to make sure I documented it here so have a record of what the fix was.
I am currently trying to add IndexedDB support to my application as alternative to WebSQL.
During refactoring I came upon an error on some pages which appears to only happen in Firefox.
Error: [$rootScope:inprog] $digest already in progress
http://errors.angularjs.org/1.4.5/$rootScope/inprog?p0=%24digest
I've tracked down the problem to the method createDatabaseConnection
where $rootScope.$apply
is called.
Funny thing is that this only occurs on pages where I use ng-include
with the onload
attribute.
<ng-include src="'js/shared/map.html'" onload="loadMap()"></ng-include>
The onload simply calls a method in the $rootscope which does some initializing for Openlayers.
I currently don't understand why this happens or if I am doing something wrong.
One workarround I found is to delay the init method via calling setTimeout
but I would be happy with something more robust and permanent.
Are the calls to $apply necessary? Or is there any way to prevent this error?
If I've got time I might try to upload an example, but I don't know if it's that easy to reproduce without the environment.
So, for a project that dynamically adds object stores it could be useful to have a upgradeDatabase
method on $indexDB
. There may be a better way (docs seem rather light on examples).
/**
@ngdoc method
@name $indexedDB.upgradeDatabase
@function
@description Upgrade the database after config.
*/
upgradeDatabase: (function (this_) {
return function (newVersion, callback) {
return closeDatabase().then(function () {
this_.upgradeDatabase(newVersion, callback);
return openDatabase();
});
};
})(this),
A really basic idea of how this could be used is below:
$indexedDB.databaseInfo().then(function (info) {
$indexedDB.upgradeDatabase(info.version + 1, function (event, db, tx) {
var objStore = db.createObjectStore('newTable', { keyPath: 'id' });
objStore.createIndex('new_idx', 'new', { unique: false });
});
});
I've never used coffeescript, but I'm sure I could guess my way through it and do a pull request if you agree this would be useful.
Any code that interacts directly with the IndexedDB API needs to be wrapped in a try...catch
block so that any errors can be passed to promise.reject()
rather than being thrown up the stack. Currently, I have to have two types of error handling logic in my code, because some errors are thrown synchronously, and others are "thrown" asynchronously via promise.reject()
. Here's an example:
var user = { id: 1, username: 'jdoe', password: 'password' };
$indexedDB.openStore('users', function(userStore) {
try {
userStore.upsert(user).then(
function(result) {
// Yay! The IDBRequest.onsuccess event fired!
},
function(err) {
// Onoes! The IDBRequest.onerror event fired!
}
);
}
catch (e) {
// Onoes! IDBObjectStore.put threw an error!
}
});
Ideally, I could get rid of the try...catch
block entirely and just write the following code instead:
var user = { id: 1, username: 'jdoe', password: 'password' };
$indexedDB.openStore('users', function(userStore) {
userStore.upsert(user).then(
function(result) {
// Yay! The IDBRequest.onsuccess event fired!
},
function(err) {
// Onoes! An error occurred! Doesn't matter whether it was IDBRequest.onerror or IDBObjectStore.put
}
);
});
I'm trying to query against an index that's multi entry (array of strings as key), and getting empty results.
Am I missing something or is it not implemented in the repository?
Here's the code I'm using:
$scope.searchStaff = function(staff){
$indexedDB.openStore('records', function(store){
$scope.lostone = staff.name;
var find = store.query();
find = find.$eq($scope.lostone);
find = find.$index("pax_idx");
store.eachWhere(find).then(function(e){
$scope.foundstaff = e;
console.log($scope.foundstaff);
});
});
};
At first,
I want to thank you for great module! You saved my time & life. ;)
I found this little bug:
$indexedDB.openStore('articles', function (store) {
var find = store.query();
find = find.$eq($routeParams.id);
find = find.$index("category_id_idx");
store.eachWhere(find).then(function (e) {
console.log('found', e);
$scope.articles = e;
}, function () {
console.log('not found');
});
});
This doesn't work, you have to:
parseInt($routeParams.id)
to make it work...
If you could place this in to manual.
Thanks!
I would like to ask how to do the update of the specific item?
Here we call @defer.reject()
with two arguments:
https://github.com/bramski/angular-indexedDB/blob/master/src/angular-indexed-db.coffee#L161-L171
The second argument of reject
method is ignored, so actual error is lost and we just get "Transaction Error" string in promise.
ok found that $rootScope.$apply
is being called.
notify: (args...) ->
$rootScope.$apply =>
@q.notify(args...)
and let's take a quick look at upsert
:
upsert: (data) ->
@_arrayOperation data, (item) =>
@store.put(item)
turns out that all array operations are using notify
.
_arrayOperation: (data, mapFunc) ->
defer = @defer()
data = [data] unless angular.isArray(data)
for item in data
req = mapFunc(item)
results = []
defer.rejectWith(req)
req.onsuccess = (e) ->
results.push(e.target.result)
defer.notify(e.target.result)
defer.resolve(results) if results.length >= data.length
if data.length == 0
return $q.when([])
defer.promise
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.