I am trying to create a simple app that allows users to select the dev who they are pairing with on that day and then a set a timer so they switch.
Currently, I am storing all the information in Firebase, but that seems like overkill.
Is there a way I can store data in the browser with JavaScript, such as in a cookie? The info I would need to store would be a small object like so:
obj1 = { name: 'rob', pairName: 'chris', timeLeft: 15 }
Or does Firebase make more sense?
I need it to persist, so that if I refresh, it does not 'reset' the data.
I am not asking how to do it, just advice and the ideal path :)
try this! =)
var DB = function(){
this.Read = function(index){
if(this.Test()){
return JSON.parse(localStorage[index]).data;
}
},
this.Write = function(index, data){
localStorage[index] = JSON.stringify({data : data});
}
this.Test = function(){
return typeof localStorage == typeof {};
}
}
// example:
var x = new DB(); // new data base
x.Write('food', ['food','bar','google']); // write data
console.log(x.Read('food')); // get data!
Related
I'm following the Progressive Web App lab from Google and it says that it's using localStorage for simplicity but that we should change it to idb.
Basically, we want to store a list of cities to display their weather information.
I tried using plain idb following the info here but I think I'm too new to this and I couldn't get any of this. Am I supposed to do:
const dbPromise = idb.open('keyval-store', 1, upgradeDB => {
upgradeDB.createObjectStore('keyval');
});
and would keyval be the name of my variable where I would use keyval.get() or keyval.set() to get and store values?
I decided to move on to the simpler idbKeyval, I'm doing:
app.saveSelectedCities = function() {
var selectedCities = JSON.stringify(app.selectedCities);
idbKeyval.set(selectedCities);
};
instead of the localStorage example:
app.saveSelectedCities = function() {
var selectedCities = JSON.stringify(app.selectedCities);
localStorage.selectedCities = selectedCities;
};
and
app.selectedCities = idbKeyval.keys().then(keys => console.log(keys)).catch(err => console.log('It failed!', err));
instead of the localStorage example:
app.selectedCities = localStorage.selectedCities;
But my app is not loading any data, and in the developer tools console, I get:
app.js:314 Uncaught ReferenceError: idbKeyval is not defined(…)
I'm sure I'm missing something trivial but these are my first steps with javascript and the likes, so please, any help with any of the points touched here would be greatly appreciated!
Given the error you're seeing, it looks like you've forgotten to include the idb-keyval library.
I too was going through this, and wanted it to work with localForage. Took a bit, because I'm new to it too, but here is what I used for the save and load functions which made it all work.
// TODO add saveSelectedCities function here
// Save list of cities to localStorage
app.saveSelectedCities = function() {
var selectedCities = JSON.stringify(app.selectedCities);
localforage.setItem('selectedCities', selectedCities);
//localStorage.selectedCities = selectedCities;
}
localforage.getItem('selectedCities').then(function(cityList) {
app.selectedCities = cityList;
app.selectedCities.forEach(function(city) {
app.getForecast(city.key, city.label);
});
}).catch(function(err) {
app.updateForecastCard(initialWeatherForecast);
app.selectedCities = [
{key: initialWeatherForecast.key, label: initialWeatherForecast.label}
];
app.saveSelectedCities();
});
I'm trying to make a Chrome extension. For that extension, I need some info that is dynamically created, but I want that data to be added even later on (on a different page).
This is some sort of data that i want to be always accessible (when the plugin runs):
var villages = new Array();
villages[0][0] = "village1";
villages[0][1] = "1325848";
villages[1][0] = "village2";
villages[1][1] = "8744351";
villages[2][0] = "village3";
villages[2][1] = "8952187";
As you can see, the array is multi-dimensional. This because I want to store the names [0] and the village id 1 together.
Does anybody knows a good way of handling this problem?
I've looked at this: jQuery Cookie
But don't know if that is a proper way of handling the problem.
Alternatively do I have to create some kind of XML file that will contain all the values?
UPDATE:
This is a skeleton example, if you want to store just village.id and village.name, just change the default data, that still works.
I have changed all code for you, you will see how to iterate array and get villages data below code.
At first I should say that It's really bad practice to save data in a multidimensional array.
You should use object, it makes your data tidy, than you can manipulate it easily.
Here is an example object for your situation,
var village = {
id: "1325848",
name : "village1"
};
console.log(village.id); //1325848
console.log(village.name); //village1
This was a basic get started example.
Here is the solution for your problem with localstorage and javascript object.
var ExtensionDataName = "myfirstextensiondata";
var ExtensionData = {
dataVersion: 3, //if you want to set a new default data, you must update "dataVersion".
villages: []
};
//default data
ExtensionData.villages.push(
{id: "1325848", name: "village1"},
{id: "8744351", name: "village2"},
{id: "8952187", name: "village3"}
);
function DB_setValue(name, value, callback) {
var obj = {};
obj[name] = value;
console.log("Data Saved!");
chrome.storage.local.set(obj, function() {
if(callback) callback();
});
}
function DB_load(callback) {
chrome.storage.local.get(ExtensionDataName, function(r) {
if (isEmpty(r[ExtensionDataName])) {
DB_setValue(ExtensionDataName, ExtensionData, callback);
} else if (r[ExtensionDataName].dataVersion != ExtensionData.dataVersion) {
DB_setValue(ExtensionDataName, ExtensionData, callback);
} else {
ExtensionData = r[ExtensionDataName];
callback();
}
});
}
function DB_save(callback) {
DB_setValue(ExtensionDataName, ExtensionData, function() {
if(callback) callback();
});
}
function DB_clear(callback) {
chrome.storage.local.remove(ExtensionDataName, function() {
if(callback) callback();
});
}
function isEmpty(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return true;
}
DB_load(function() {
//YOUR MAIN CODE WILL BE HERE
console.log(ExtensionData);
console.log(ExtensionData.villages); //array of villages
console.log(ExtensionData.villages[0]); //first village object
console.log(ExtensionData.villages[0].id); //first village id
console.log(ExtensionData.villages[0].name); //first village name
//HOW TO ITERATE VILLAGES
for (var i = 0; i < ExtensionData.villages.length; i++) {
console.log(ExtensionData.villages[i].id); //village id
console.log(ExtensionData.villages[i].name); //village name
}
});
QUESTIONS:
Does the ExtensionDataName to be the same? or can i change that?
ExtensionDataName is used as a name when your data is saved to localstorage, it's just a name of your data collection. Therefore of course you can change it, do what you want, it's up to you.
what is the goal that you achief when you change the number of this line:
dataVersion: 3, //if you want to set a new default data, you must update "dataVersion"?
At the first time when user run this extension there is no data in the localstorage. So default village list is used,
In my example default village list is,
[
{id: "1325848", name: "village1"},
{id: "8744351", name: "village2"},
{id: "8952187", name: "village3"}
]
this default list is saved to localstorage. After than when extension runs again(not first time), the default list is not important anymore, because there is a village list stored in localstorage, so it loads village list from localstorage.
For example if you want to add a new village during the runtime of extension you can do it like this,
ExtensionData.villages.push({id: "1215555", name: "village4"});
DB_save();
So what is the goal of dataVersion?
If you look DB_load() function, it's used there. It checks whether dataVersion is still same, If it's not same, It decides that
"There is a updated default data so I should clear localstorage and reload new data to localstorage"
So If you don't change this lines,
ExtensionData.villages.push(
{id: "1325848", name: "village1"},
{id: "8744351", name: "village2"},
{id: "8952187", name: "village3"}
);
Than you won't change dataVersion
I have tried to get some information from W3C regarding the update of an objectStore item in a indexedDB database, but with not so much susccess.
I found here a way to do it, but it doesn't really work for me.
My implementation is something like this
DBM.activitati.edit = function(id, obj, callback){
var transaction = DBM.db.transaction(["activitati"], IDBTransaction.READ_WRITE);
var objectStore = transaction.objectStore("activitati");
var keyRange = IDBKeyRange.only(id);
objCursor = objectStore.openCursor(keyRange);
objCursor.onsuccess = function(e){
var cursor = e.target.result;
console.log(obj);
var request = cursor.update(obj);
request.onsuccess = function(){
callback();
}
request.onerror = function(e){
conosole.log("DBM.activitati.edit -> error " + e);
}
}
objCursor.onerror = function(e){
conosole.log("DBM.activitati.edit -> error " + e);
}
}
I have all DBM.activitati.(add | remove | getAll | getById | getByIndex) methods working, but I can not resolve this.
If you know how I can manage it, please, do tell!
Thank you!
Check out this jsfiddle for some examples on how to update IDB records. I worked on that with another StackOverflower -- it's a pretty decent standalone example of IndexedDB that uses indexes and does updates.
The method you seem to be looking for is put, which will either insert or update a record if there are unique indexes. In that example fiddle, it's used like this:
phodaDB.indexedDB.addUser = function(userObject){
//console.log('adding entry: '+entryTxt);
var db = phodaDB.indexedDB.db;
var trans = db.transaction(["userData"],IDBTransaction.READ_WRITE);
var store = trans.objectStore("userData");
var request = store.put(userObject);
request.onsuccess = function(e){
phodaDB.indexedDB.getAllEntries();
};
request.onerror = function(e){
console.log('Error adding: '+e);
};
};
For what it's worth, you've got some possible syntax errors, misspelling "console" in console.log as "conosole".
A bit late for an answer, but possible it helps others. I still stumbled -as i guess- over the same problem, but it's very simple:
If you want to INSERT or UPDATE records you use objectStore.put(object) (help)
If you only want to INSERT records you use objectStore.add(object) (help)
So if you use add(object), and a record key still exists in DB, it will not overwritten and fires error 0 "ConstraintError: Key already exists in the object store".
If you use put(object), it will be overwritten.
this is case of update infos of an user object
var transaction = db.transaction(["tab_user"], "readwrite");
var store = transaction.objectStore("tab_user");
var req = store.openCursor();
req.onerror = function(event) {
console.log("case if have an error");
};
req.onsuccess = function(event) {
var cursor = event.target.result;
if(cursor){
if(cursor.value.idUser == users.idUser){//we find by id an user we want to update
var user = {};
user.idUser = users.idUser ;
user.nom = users.nom ;
var res = cursor.update(user);
res.onsuccess = function(e){
console.log("update success!!");
}
res.onerror = function(e){
console.log("update failed!!");
}
}
cursor.continue();
}
else{
console.log("fin mise a jour");
}
}
I'm a couple of years late, but thought it'd be nice to add my two cents in.
First, check out BakedGoods if you don't want to deal with the complex IndexedDB API.
It's a library which establishes a uniform interface that can be used to conduct storage operations in all native, and some non-native client storage facilities. It also maintains the flexibility and options afforded to the user by each. Oh, and it's maintained by yours truly :) .
With it, placing one or more data items in an object store can be as simple as:
bakedGoods.set({
data: [{key: "key1", value: "value1"}, {key: "key2", value: "value2"}),
storageTypes: ["indexedDB"],
complete: function(byStorageTypeResultDataObj, byStorageTypeErrorObj){}
});
Now to answer the actual question...
Lets begin by aggregating the valuable information spread across the existing answers:
IDBObjectStore.put() adds a new record to the store, or updates an existing one
IDBObjectStore.add() adds a new record to the store
IDBCursor.update() updates the record at the current position of the cursor
As one can see, OP is using an appropriate method to update a record. There are, however, several things in his/her code, unrelated to the method, that are incorrect (with respect to the API today at least). I've identified and corrected them below:
var cursorRequest = objectStore.openCursor(keyRange); //Correctly define result as request
cursorRequest.onsuccess = function(e){ //Correctly set onsuccess for request
var objCursor = cursorRequest.result; //Get cursor from request
var obj = objCursor.value; //Get value from existing cursor ref
console.log(obj);
var request = objCursor.update(obj);
request.onsuccess = function(){
callback();
}
request.onerror = function(e){
console.log("DBM.activitati.edit -> error " + e); //Use "console" to log :)
}
}
cursorRequest.onerror = function(e){ //Correctly set onerror for request
console.log("DBM.activitati.edit -> error " + e); //Use "console" to log :)
}
I want to update a div with a list of anchors that I generate from a local database in chrome. It's pretty simple stuff, but as soon as I try to add the data to the main.js file via a callback everything suddenly becomes undefined. Or the array length is set to 0. ( When it's really 18. )
Initially, I tried to install it into a new array and pass it back that way.
Is there a setting that I need to specify in the chrome manifest.json in order to allow for communication with the database API? I've checked, but all I've been able to find was 'unlimited storage'
The code is as follows:
window.main = {};
window.main.classes = {};
(function(awe){
awe.Data = function(opts){
opts = opts || new Object();
return this.init(opts);
};
awe.Data.prototype = {
init:function(opts){
var self = this;
self.modified = true;
var db = self.db = openDatabase("buddy","1.0","LocalDatabase",200000);
db.transaction(function(tx){
tx.executeSql("CREATE TABLE IF NOT EXISTS listing ( name TEXT UNIQUE, url TEXT UNIQUE)",[],function(tx,rs){
$.each(window.rr,function(index,item){
var i = "INSERT INTO listing (name,url)VALUES('"+item.name+"','"+item.url+"')";
tx.executeSql(i,[],null,null);
});
},function(tx,error){
});
});
self._load()
return this;
},
add:function(item){
var self = this;
self.modified = true;
self.db.transaction(function(tx){
tx.executeSql("INSERT INTO listing (name,url)VALUES(?,?)",[item.name,item.url],function(tx,rs){
//console.log('success',tx,rs)
},function(tx,error){
//console.log('error',error)
})
});
self._load()
},
remove:function(item){
var self = this;
self.modified = true;
self.db.transaction(function(tx){
tx.executeSql("DELETE FROM listing where name='"+item.name+"'",[],function(tx,rs){
//console.log('success',tx,rs)
},function(tx,error){
//console.log('error',tx,error);
});
});
self._load()
},
_load:function(callback){
var self = this;
if(!self.modified)
return;
self.data = new Array();
self.db.transaction(function(tx){
tx.executeSql('SELECT name,url FROM listing',[],function(tx,rs){
console.log(callback)
for(var i = 0; i<rs.rows.length;i++)
{
callback(rs.rows.item(i).name,rs.rows.item(i).url)
// var row = rs.rows.item(i)
// var n = new Object()
// n['name'] = row['name'];
// n['url'] = row['url'];
}
},function(tx,error){
//console.log('error',tx,error)
})
})
self.modified = false
},
all:function(cb){
this._load(cb)
},
toString:function(){
return 'main.Database'
}
}
})(window.main.classes);
And the code to update the list.
this.database.all(function(name,url){
console.log('name','url')
console.log(name,url)
var data = []
$.each(data,function(index,item){
try{
var node = $('<div > '+item.name + '</div>');
self.content.append(node);
node.unbind();
node.bind('click',function(evt){
var t = $(evt.target).attr('href');
chrome.tabs.create({
"url":t
},function(evt){
self._tab_index = evt.index
});
});
}catch(e){
console.log(e)
}
})
});
From looking at your code above, I notice you are executing "self._load()" at the end of each function in your API. The HTML5 SQL Database is asynchronous, you can never guarantee the result. In this case, I would assume the result will always be 0 or random because it will be a race condition.
I have done something similar in my fb-exporter extension, feel free to see how I have done it https://github.com/mohamedmansour/fb-exporter/blob/master/js/database.js
To solve a problem like this, did you check the Web Inspector and see if any errors occurs in the background page. I assume this is all in a background page eh? Try to see if any error occurs, if not, I believe your encountering a race condition. Just move the load within the callback and it should properly call the load.
Regarding your first question with the unlimited storage manifest attribute, you don't need it for this case, that shouldn't be the issue. The limit of web databases is 5MB (last I recall, it might have changed), if your using a lot of data manipulation, then you use that attribute.
Just make sure you can guarantee the this.database.all is running after the database has been initialized.
SessionStorage and LocalStorage allows to save key/value pairs in a web browser. The value must be a string, and save js objects is not trivial.
var user = {'name':'John'};
sessionStorage.setItem('user', user);
var obj = sessionStorage.user; // obj='[object Object]' Not an object
Nowadays, you can avoid this limitation by serializing objects to JSON, and then deserializing them to recover the objects. But the Storage API always pass through the setItem and getItem methods.
sessionStorage.setItem('user', JSON.stringify(user));
var obj = JSON.parse(sessionStorage.getItem('user')); // An object :D
Can I avoid this limitation?
I just want to execute something like this:
sessionStorage.user.name; // 'John'
sessionStorage.user.name = 'Mary';
sessionStorage.user.name // 'Mary'
I have tried the defineGetter and defineSetter methods to intercept the calls but its a tedious job, because I have to define all properties and my target is not to know the future properties.
The solution is to stringify the object before calling setItem on the sessionStorage.
var user = {'name':'John'};
sessionStorage.setItem('user', JSON.stringify(user));
var obj = JSON.parse(sessionStorage.user);
Could you not 'stringify' your object...then use sessionStorage.setItem() to store that string representation of your object...then when you need it sessionStorage.getItem() and then use $.parseJSON() to get it back out?
Working example http://jsfiddle.net/pKXMa/
Either you can use the accessors provided by the Web Storage API or you could write a wrapper/adapter. From your stated issue with defineGetter/defineSetter is sounds like writing a wrapper/adapter is too much work for you.
I honestly don't know what to tell you. Maybe you could reevaluate your opinion of what is a "ridiculous limitation". The Web Storage API is just what it's supposed to be, a key/value store.
This is a dynamic solution which works with all value types including objects :
class Session extends Map {
set(id, value) {
if (typeof value === 'object') value = JSON.stringify(value);
sessionStorage.setItem(id, value);
}
get(id) {
const value = sessionStorage.getItem(id);
try {
return JSON.parse(value);
} catch (e) {
return value;
}
}
}
Then :
const session = new Session();
session.set('name', {first: 'Ahmed', last : 'Toumi'});
session.get('name');
Use case:
sesssionStorage.setObj(1,{date:Date.now(),action:'save firstObject'});
sesssionStorage.setObj(2,{date:Date.now(),action:'save 2nd object'});
//Query first object
sesssionStorage.getObj(1)
//Retrieve date created of 2nd object
new Date(sesssionStorage.getObj(1).date)
API
Storage.prototype.setObj = function(key, obj) {
return this.setItem(key, JSON.stringify(obj))
};
Storage.prototype.getObj = function(key) {
return JSON.parse(this.getItem(key))
};
var user = {'name':'John'};
sessionStorage['user'] = JSON.stringify(user);
console.log(sessionStorage['user']);
Session storage cannot support an arbitrary object because it may contain function literals (read closures) which cannot be reconstructed after a page reload.
You can create 2 wrapper methods for saving and retrieving object from session storage.
function saveSession(obj) {
sessionStorage.setItem("myObj", JSON.stringify(obj));
return true;
}
function getSession() {
var obj = {};
if (typeof sessionStorage.myObj !== "undefined") {
obj = JSON.parse(sessionStorage.myObj);
}
return obj;
}
Use it like this:- Get object, modify some data, and save back.
var obj = getSession();
obj.newProperty = "Prod"
saveSession(obj);
You could also use the store library which performs it for you with crossbrowser ability.
example :
// Store current user
store.set('user', { name:'Marcus' })
// Get current user
store.get('user')
// Remove current user
store.remove('user')
// Clear all keys
store.clearAll()
// Loop over all stored values
store.each(function(value, key) {
console.log(key, '==', value)
})
CustomHook is one of the best ways to store a token as an object by using sessionStorage in React 17.0.1.
Follow these instructions:
Implement sessionStorage get/set inside a custom function:
export default function useToken() {
const getToken = () => {
const tokenString = sessionStorage.getItem('token');
const userToken = JSON.parse(tokenString);
return userToken?.token;
};
const [token, setToken] = useState(getToken());
const saveToken = (userToken) => {
sessionStorage.setItem('token', JSON.stringify(userToken));
setToken(userToken.token);
};
return {
setToken: saveToken,
token,
};
}
This function can be imported and used as a hook in other components like:
import useToken from './useToken';
const { token, setToken} = useToken(); // call CustomHook
const handleSubmit = async (event) => {
event.preventDefault();
// call functionAPI
const token = await loginUser({
username,
password,
});
setToken(token); // AUTOMATICALLY token will be stored to sessionStorage of browser
};