I am not able to retrieve properties when looping through objects stored in indexedDB. When i attempt to get objects trough tasksStore.get(i);, i get the error Cannot read property 'property' of undefined at IDBRequest.getTasks.onsuccess, however, if I change it to tasksStore.get(1);, it works fine, and gets object with id=1 x index's length.
I've tried checking the typeof of both ways, and they both return number.
//success handler on connection
request.onsuccess = function(e) {
db = request.result;
//define store index
tasksStore = tasksTx.objectStore("tasksStore");
//error handler on result of the request
db.onerror = function(e) {
console.log("ERROR " + e.target.errorCode);
}
//variable for counting objects in the index
let amountOfTasks = tasksIndex.count();
//error handler
amountOfTasks.onerror = function() {
console.log("There was an error finding the amount of tasks")
}
//success handler
amountOfTasks.onsuccess = function() {
for (var i = 1; i < amountOfTasks.result; i++) {
let getTasks = tasksStore.get(i);
let getTasksElementContainer = document.getElementById("list-tasks");
let createTasksList = document.createElement("li");
createTasksList.id = "task-" + i;
getTasks.onerror = function() {
console.log("There was an error looping through the tasks")
}
getTasks.onsuccess = function() {
console.log(getTasks.result.title); //getTasks.result works, getTasks.result.title does not.
getTasksElementContainer.appendChild(createTasksList);
//JSON stringify to return object in string format, and not [Object object]
createTasksList.innerHTML = JSON.stringify(getTasks.result.title);
}
}
}
}
When you call IDBObjectStore.get with a key that doesn't exist in your database, the resulting value is undefined. That likely explains why sometimes getTasks.result is undefined.
If you're still having trouble, you'd likely be well served by making a self-contained reproducible example. You'll probably find your own bug in the process of doing that. If not, it's easier to get more specific help on Stack Overflow if you have some code that other people can run to directly observe the problem (so including database creation and inserting data).
Related
I am currently trying to get an indexedDB database running. However, I am struggling with some issues regarding indexedDB's put method. Although the keypath is defined and the JSONObject that is handed over contains a value which is named in the same way as the defined keypath, the put method causes the following error:
Uncaught DOMException: Failed to execute 'put' on 'IDBObjectStore': Evaluating the object store's key path did not yield a value.
In order to make sure that the JSONObject really contains the value that shall be used as the key, I am logging the object. Thats what it looks like:
{"key":102019,"month":10,"year":2019,"spendings":[{"type":{"name":"Technology","importance":70,"iconURL":"./Resources/Technology.png"},"cost":"1500","name":"Macbook pro","timestamp":1571696285911}],"budget":0}
The code that is being used to store the data is the following:
function callbackSaveSpendingMonth(database, spendingMonth) {
let userName = defaultUserName;
let transaction = database.transaction(userName, "readwrite");
let objectStore = transaction.objectStore(userName, { keyPath: 'key' });
let JSONspendingMonth = JSON.stringify(spendingMonth);
console.log(JSONspendingMonth);
let request = objectStore.put(JSONspendingMonth);
request.onsuccess = function (event) {
console.log("The month " + spendingMonth.getMonth() + "/" + spendingMonth.getYear() + " has been saved successfully!");
}
transaction.oncomplete = function (event) {
console.log("A connection to indexedDB has successfully been established!");
}
}
On the indexeddb i want to look if there is a key permanent and do some actions. But if not, i want to make some other actions. I can do the actions if the permanent is there, however when it is not I can get the onerror to work. Is the onerror suppose to do this thing? How can I check if there is not value in it?
var hashtype = 'permanent';
var getPermanent = store.get(hashtype);
getPermanent.onsuccess = function() {
var ivrame = getPermanent.result.value;
};
getPermanent.onerror = function() {
console.log('onerror')
};
See the note under https://w3c.github.io/IndexedDB/#dom-idbobjectstore-get - the get method yields success with undefined if there is no matching record.
So you have a few options:
Use get(key) and test the result for undefined. This works unless undefined is a value you expect to store (it's a valid value)
Use count(key) - the result will be 1 if present, 0 if absent. Easy if you're just testing for existence, but doesn't get you the record.
Use openCursor(key) and test to see if the request's result is a cursor (record present as request.result.value) or undefined (no record in range)
For your code:
var hashtype='permanent';
// #1: Use get
var getPermanent = store.get(hashtype);
getPermanent.onsuccess = function() {
if (getPermanent.result === undefined) {
// no record with that key
} else {
var value = getPermanent.result;
}
};
// #2: Use count
var getPermanent = store.count(hashtype);
getPermanent.onsuccess = function() {
if (getPermanent.result === 0) {
// no record with that key
} else {
...
}
};
// #3: Use cursor
var getPermanent = store.openCursor(hashtype);
getPermanent.onsuccess = function() {
var cursor = getPermanent.result;
if (!cursor) {
// no record with that key
} else {
var value = cursor.value;
}
};
The function assigned to request.onsuccess is a callback function that is always called, regardless of whether the value is present in the store. When there is no corresponding object in the store, the result object will be undefined. When there is a corresponding object in the store, the result object will be defined. So you simply need to check if the object is defined from within the onsuccess callback function.
request.onerror is a separate callback from request.onsuccess. onerror gets called when there is some type of failure in indexedDB (e.g. something like you tried to get a value from a store that doesn't exist, or you tried to put a duplicate object into a store that doesn't permit duplicates). request.onerror does not get called when no value is found as a result of calling store.get, because that is not considered an 'error' in the failure sense.
So, what you want to do is something like this:
var hashtype='permanent';
var getPermanent = store.get(hashtype);
getPermanent.onsuccess = function(event) {
//var ivrame=getPermanent.result.value;
var result = getPermanent.result;
if(result) {
console.log('Got a result!', result);
var ivrame = result;
} else {
console.log('Result was undefined! No matching object found');
}
};
getPermanent.onerror = function() {
console.log('Something went wrong trying to perform the get request');
};
Do not try and access request.result.value. There is no such thing in the case of a get request. When using store.get, request.result contains the matching object you want, or is undefined. When using store.openCursor, request.result contains the cursor, which is defined if there is at least one matching object and you have not already iterated past it. To get the matching object at the cursor's current position, you would use cursor.value. Here, cursor.value will always be defined, because cursor would otherwise be undefined, and you would obviously check for that beforehand.
Instead of using getPermanent.result to access data provided by 'get' request it is better to use event.target.result. It also can be compared with undefined to check absence of requested key:
db = this.result;
var tr = db.transaction("data");
var objstore = tr.objectStore("data");
var getres = objstore.get(0);
getres.onsuccess = function(event)
{
if(event.target.result.data === undefined)
console.log("key not found");
}
Again, i got some question on indexeddb. I´m getting a
InvalidStateError: A Mutation operation was attempted on a database
that did not allow mutations.
and also an
AbortError
Here is my code:
DB_LINK.prototype.pushStoreNumeric = function ()
{
// Saving Values
var _temp = 0;
var _version = this.link.version;
var _name = this.link.name;
var that = this;
var _objectStoreNames = this.link.objectStoreNames;
// Close DB
this.link.close();
this.state = 4;
// Reopen Database
this.req = indexedDB.open(_name,_version+1); // Abort error here
this.req.onupgradeneeded = function () {
that.state = 1;
// Get Number of object stores
_temp = _objectStoreNames.length;
if(_temp != 0)
{
// Already object stores: read highest value
_temp = parseInt(_objectStoreNames[_objectStoreNames.length - 1]);
}
that.link.createObjectStore(_temp); // InvalidStateError here
};
I have marked per comment where the errors occur.
The InvalidStateError occures first, the AbortError follows.
I am calling this function inside another onsuccess function of the same database. Might this be the problem?
What is this.link? That's probably the problem. You need to be doing createObjectStore on the database instance created by the indexedDB.open request. So either this.req.result.createObjectStore or (if you change to this.req.onupgradeneeded = function (e) {) you could use e.target.result.createObjectStore.
More generally, I can't really comment on what your code is supposed to be doing because I can only see a snippet, but it looks really weird how you are incrementing the version every time this is called. Probably you don't actually want to be doing that. You might want to read a bit more documentation.
EDIT: it seems that value[i].extrainfoimage is only undefined within imageRef.child("-JlSvEAw......
I've read through the documentation twice so far and still haven't been able to figure this one out.
Basically I load some data from firebase and set it as an array. I iterate through that array and intend to replace some properties, as I iterate, with data I get from other places in my Firebase DB. However everytime I go to replace the properties I get
"Uncaught TypeError: Cannot set property 'extrainfoimage' of undefined"
This is my code:
var questionsRef = new Firebase("https://XX.firebaseio.com/Questions");
var imageRef = new Firebase("https://XX.firebaseio.com/Images");
//get the first 6 items
var query = questionsRef.orderByChild('date_time_asked').limitToFirst(6);
//get them as a firebase array promise
$scope.questions = $firebaseArray(query);
//wait for it to load
$scope.questions.$loaded().then(function (value) {
//iterate through
for (i = 0; i < value.length; i++) {
//check if there is data to be replaced
if (value[i].extrainfoimage) {
//if there is fetch it from firebase and replace
imageRef.child("-JlSvEAwu5-WZJkOE_b/image").once("value",function(data){
value[i].extrainfoimage = data.val();
});
}
}
})
Possibly its becuase the last item in value doesn't have extrainfoimage. Because your set for value[i].extrainfoimage is async, it doesn't capture the correct i value, and therefore fails when it is executing.
Try to wrap it in a IIFE:
for (i = 0; i < value.length; i++) {
//check if there is data to be replaced
if (value[i].extrainfoimage) {
(function(curr) {
//if there is fetch it from firebase and replace
imageRef.child("-JlSvEAwu5-WZJkOE_b/image").once("value",function(data){
value[curr].extrainfoimage = data.val();
});
})(i);
}
}
I'm developing a small Chrome extension that would allow me to save some records to chrome.storage and then display them.
I've managed to make the set and get process work as I wanted (kinda), but now I'd like to add a duplicate check before saving any record, and I'm quite stuck trying to find a nice and clean solution.
That's what I came up for now:
var storage = chrome.storage.sync;
function saveRecord(record) {
var duplicate = false;
var recordName = record.name;
storage.get('records', function(data) {
var records = data.records;
console.log('im here');
for (var i = 0; i < records.length; i++) {
var Record = records[i];
if (Record.name === recordName) {
duplicate = true;
break;
} else {
console.log(record);
}
}
if (duplicate) {
console.log('this record is already there!');
} else {
arrayWithRecords.push(record);
storage.set({ bands: arrayWithRecords }, function() {
console.log('saved ' + record.name);
});
}
});
}
I'm basically iterating on the array containing the records and checking if the name property already exists. The problem is it breaks basic set and get functionality -- in fact, when saving it correctly logs 'im here' and the relative record object, but it doesn't set the value. Plus, after a while (generally after trying to list the bands with a basic storage.get function) it returns this error:
Error in response to storage.get: TypeError: Cannot read property
'name' of null
I'm guessing this is due to the async nature of the set and get and my incompetence working with it, but I can't get my head around it in order to find a better alternative. Ideas?
Thanks in advance.