initialize array that is within a object, use in localStorage - javascript

Isolated Question:
user[username] = {
cool: true,
stuff: false,
arrData: []
};
I am trying to globally initialize my arrData array in order to populate in different scope; the below does not work.
let user.arrData = [],
later I want to do the following, for context:
...........
inputs.forEach(function(input){
user = JSON.parse(localStorage.getItem(username)); // tried moving this here
user.arrData.push({ id: input.id, checked: input.checked });
// the above errors on .arrData cannot push undefined, I have tried also including the key here, how is this done?
});
localStorage.setItem(user, JSON.stringify(user.arrData));
console.log({ arrData: user.arrData }); // returns undefined
............
Sub question: can I set and retrieve a object.array with localStorage like this. Meaning my reference to (user.arrData)?
Context/actual code:
Below is the complete relevant code for context: I am trying to save a series of checkbox states per user with local storage, then reload them when a button is clicked.
let user = {};
var savebtnBM = document.getElementById("savebtnBM");
const username = document.querySelector("span.name").textContent;
user[username] = {
cool: true,
stuff: false,
arrData: []
};
var layersMod = document.querySelectorAll(".layers #savebtnBM")[0];
layersMod.addEventListener('click', ()=>{
setTimeout(
function() {
Saver();
}, 2000);
});
function Saver() {
var inputs = document.querySelectorAll('input[type="checkbox"]');
// var arrData = [];
inputs.forEach(function(input){
user[username].arrData.push({ id: input.id, checked: input.checked });
});
localStorage.setItem(user, JSON.stringify(user.arrData));
console.log(JSON.stringify(user.arrData));
}
var etHOME= document.getElementById("et-phone-home");
etHOME.addEventListener('click', ()=>{
user = JSON.parse(localStorage.getItem(username));
console.log(user);
document.getElementsByClassName("big-button")[0].click();
setTimeout(load_, 2000);
function load_() {
var inputs = JSON.parse(localStorage.getItem(user));
inputs.forEach(function(input){
document.getElementById(input.id).checked = input.checked;
load__();
});
function load__() {
var elems = document.querySelectorAll('input:checked');
for(var i = 0; i<elems.length; i++) {
elems[i].click();
}
}
}
});

Sub question response:
The localStorage and sessionStorage can only handle strings.
So first we must convert our object to string with JSON.stringify() as in the following example:
localStorage.setItem('user', JSON.stringify(user));
When it comes to obtaining the previously saved we do the opposite, using JSON.parse():
//...
const storedUser = JSON.parse(localStorage.getItem('user'));
console.log({ arrData: storedUser.arrData });

Related

localStorage array.push

Could someone tell me how to push elements into an array in localStorage?
My code:
(localStorage.getItem('projects') === null) ? localStorage.setItem('projects', ['proj1', 'proj2', 'proj3']) : '';
var ItemGet = localStorage.getItem('projects');
function CreateObject() {
console.log(ItemGet);
var Serializable = JSON.parse(ItemGet);
Serializable.push('proj4');
console.log(ItemGet);
}
<button onclick="CreateObject()">Add Object</button>
General approach:
let old_data = JSON.parse(localStorage.getItem('projects'))
let new_data = old_data.push(some_new_data)
localStorage.setItem('projects',JSON.stringify(new_data))
I would do the following assuming that your data is not a multiDimensional array.
(localStorage.getItem('projects') === null) ? localStorage.setItem('projects',
JSON.stringify(['proj1', 'proj2', 'proj3'])) : '';
var ItemGet = localStorage.getItem('projects');
function CreateObject() {
var Serializable = JSON.parse(ItemGet);
Serializable.push('proj4');
localStorage.setItem('projects',JSON.stringify(Serializable));
}
The problem you are hitting is that data stored in localStorage has to be a string. You'll have to parse/stringify before settting/getting anything from local storage. If you didn't want to work with strings, you may find something like IndexedDB API
const stuff = [ 1, 2, 3 ];
// Stringify it before setting it
localStorage.setItem('stuff', JSON.stringify(stuff));
// Parse it after getting it
JSON.parse(localStorage.getItem('stuff'));
Here is an example of using IndexedDB API from the docs
const dbName = "the_name";
var request = indexedDB.open(dbName, 2);
request.onerror = function(event) {
// Handle errors.
};
request.onupgradeneeded = function(event) {
var db = event.target.result;
// Create an objectStore to hold information about our customers. We're
// going to use "ssn" as our key path because it's guaranteed to be
// unique - or at least that's what I was told during the kickoff meeting.
var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
// Create an index to search customers by name. We may have duplicates
// so we can't use a unique index.
objectStore.createIndex("name", "name", { unique: false });
// Create an index to search customers by email. We want to ensure that
// no two customers have the same email, so use a unique index.
objectStore.createIndex("email", "email", { unique: true });
// Use transaction oncomplete to make sure the objectStore creation is
// finished before adding data into it.
objectStore.transaction.oncomplete = function(event) {
// Store values in the newly created objectStore.
var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
customerData.forEach(function(customer) {
customerObjectStore.add(customer);
});
};
};
There are also other solutions out there like PouchDB depending on your needs
Say for example you have an array. This is how you can store it in the local storage.
let my_array = [1, 2, 3, 4];
localStorage.setItem('local_val', JSON.stringify(my_array))
Now to push any data into the local storage array you have to override by the new data like bellow
let oldArray = JSON.parse(localStorage.getItem('local_val'))
oldArray.push(1000)
localStorage.setItem('local_val', JSON.stringify(oldArray))

Loading data correctly on handsontable with Meteor and blaze

I'm using handsontable on Meteor 1.4.1 through the plugin awsp:handsontable#0.16.1
The problem I have is that the matrix gets re-rendered every time I change a value, which creates two issues. The first is that the focus of the edited cell gets lost and the scroll goes back to the top. The second is that sometimes the values are not saved because the data of the matrix get reloaded with each change.
The way I'm subscribing to the data and rendering the table is as follows:
Template.connectivityMatrix.onCreated(function () {
this.activeScenario = () => Session.get('active_scenario');
this.autorun(() => {
this.subscribe('connectivityMatrixUser', this.activeScenario());
});
});
Template.connectivityMatrix.onRendered(function () {
this.autorun(() => {
if (this.subscriptionsReady()) {
const activeScenario = Session.get('active_scenario');
const currentScenario = Scenarios.findOne({_id: activeScenario});
const currentTurn = currentScenario.turn;
const numObj = ConnectivityMatrix.find({scenario_id: activeScenario, user_id: Meteor.userId(), turn: currentTurn}).count();
var myData = []; // Need this to create instance
var container = document.getElementById('connectivity-matrix');
var hot = new Handsontable(container, { // Create Handsontable instance
data: myData,
startRows: numObj,
startCols: numObj,
afterChange: function (change, source) { // 'change' is an array of arrays.
if (source !== 'loadData') { // Don't need to run this when data is loaded
for (i = 0; i < change.length; i++) { // For each change, get the change info and update the record
var rowNum = change[i][0]; // Which row it appears on Handsontable
var row = myData[rowNum]; // Now we have the whole row of data, including _id
var key = change[i][1]; // Handsontable docs calls this 'prop'
var oldVal = change[i][2];
var newVal = change[i][3];
var setModifier = {$set: {}}; // Need to build $set object
setModifier.$set[key] = newVal; // So that we can assign 'key' dynamically using bracket notation of JavaScript object
ConnectivityMatrix.update(row._id, setModifier);
}
}
}
});
myData = ConnectivityMatrix.find({scenario_id: activeScenario, turn: currentTurn, user_id: Meteor.userId()}, {sort: {created_at: 1}}).fetch(); // Tie in our data
hot.loadData(myData);
}
});
});
What I want to achieve is to create the matrix only once instead of recreate it with each data change so the focus stays and the data gets always saved.
So I've tried leaving only the last two lines inside the block of this.autorun() as suggested in this question
Template.connectivityMatrix.onRendered(function () {
const activeScenario = Session.get('active_scenario');
const currentScenario = Scenarios.findOne({_id: activeScenario});
const currentTurn = currentScenario.turn;
const numObj = ConnectivityMatrix.find({scenario_id: activeScenario, user_id: Meteor.userId(), turn: currentTurn}).count();
var hot = new Handsontable(container, { // Create Handsontable instance
...
});
this.autorun(() => {
if (this.subscriptionsReady()) {
myData = ConnectivityMatrix.find({scenario_id: activeScenario, turn: currentTurn, user_id: Meteor.userId()}, {sort: {created_at: 1}}).fetch(); // Tie in our data
hot.loadData(myData);
}
});
});
but then the first time I load the page, the data is not available so I get the error
Cannot read property 'turn' of undefined
Therefore, how can I properly get all the data needed to create the table without re-rendering it when a cell value changes?
Thanks in advance for any help.
You are trying to query the Scenarios and ConnectivityMatrix collections before they are ready. Move all mongo queries inside your this.subscriptionsReady() conditional block.
The way I managed to do what I needed is by stopping the computation after the matrix gets rendered. The code is as follows:
Template.connectivityMatrix.onRendered(function () {
this.autorun((computation) => {
if (this.subscriptionsReady()) {
const currentScenario = Scenarios.findOne({_id: activeScenario});
const currentTurn = currentScenario.turn;
const numObj = ConnectivityMatrix.find({scenario_id: activeScenario, user_id: Meteor.userId(), turn: currentTurn}).count();
var myData = []; // Need this to create instance
var container = document.getElementById('connectivity-matrix');
var hot = new Handsontable(container, { // Create Handsontable instance
data: myData,
colHeaders: arrayRowsCols,
rowHeaders: arrayRowsCols,
height: '450',
maxRows: numObj,
maxCols: numObj,
columns: columns,
afterChange: function (change, source) { // 'change' is an array of arrays.
if (source !== 'loadData') { // Don't need to run this when data is loaded
for (i = 0; i < change.length; i++) { // For each change, get the change info and update the record
var rowNum = change[i][0]; // Which row it appears on Handsontable
var row = myData[rowNum]; // Now we have the whole row of data, including _id
var key = change[i][1]; // Handsontable docs calls this 'prop'
var oldVal = change[i][2];
var newVal = change[i][3];
var setModifier = {$set: {}}; // Need to build $set object
setModifier.$set[key] = newVal; // So that we can assign 'key' dynamically using bracket notation of JavaScript object
ConnectivityMatrix.update(row._id, setModifier);
}
}
}
});
myData = ConnectivityMatrix.find({scenario_id: activeScenario, turn: currentTurn, user_id: Meteor.userId()}, {sort: {created_at: 1}}).fetch(); // Tie in our data
hot.loadData(myData);
computation.stop();
}
});
});

Saving data in localstorage

I'm try to save data to localstorage. Created "class" through constructor and try to put get and set methods to them. But when I click my button(when button was clicked data must be saving) nothing happens(In developer tools "Resource" tab). When I tried simple save data through JSON.stringify and else all was worked.
(function() {
window.onload = function() {
document.getElementById('buttonCreate').onclick = function() {
var topicValue = document.getElementById("create-topic").value;
var statusValue = document.getElementById("create-status").value;
var descriptionValue = document.getElementById("create-description").value;
var storage = new Storage();
var ticket = {
topic: topicValue,
status: statusValue,
description: descriptionValue
};
storage.set("Item", item);
}
}
})();
"class" Storage:
function Storage() {
this._ITEMS_DESCRIPTOR = 'items';
}
Storage.prototype.get = function() {
var fromStorage = localStorage.getItem(this._ITEMS_DESCRIPTOR);
return fromStorage ? JSON.parse(fromStorage) : [];
};
Storage.prototype.set = function(key, items) {
localStorage.setItem(key, JSON.stringify(items));
};
The exact issue with your code is the storage key and also the item that you are trying to store which is not defined.
It stores it in to Item key and the get method is written to take it from the key, items.
by looking at the given code, you should suppose to store ticket object. storage.set(ticket);
A suggested better approach: To pass a key to instantiate Storage object and then use it accordingly.
Such as var storage = new Storage('Item');
(function() {
window.onload = function() {
document.getElementById('buttonCreate').onclick = function() {
var topicValue = document.getElementById("create-topic").value;
var statusValue = document.getElementById("create-status").value;
var descriptionValue = document.getElementById("create-description").value;
var storage = new Storage("ticket");
var ticket = {
topic: topicValue,
status: statusValue,
description: descriptionValue
};
storage.set(ticket);
}
}
})();
"class" Storage:
function Storage(key) {
this._ITEMS_DESCRIPTOR = key;
}
Storage.prototype.get = function() {
var fromStorage = localStorage.getItem(this._ITEMS_DESCRIPTOR);
return fromStorage ? JSON.parse(fromStorage) : {};
};
Storage.prototype.set = function(item) {
localStorage.setItem(this._ITEMS_DESCRIPTOR, JSON.stringify(item));
};
To get the ticket value stored in localstorage:
var storage = new Storage('ticket');
var ticket = storage.get();
Per Mike McCaughan's comment, you were referencing an undefined variable.
Using strict mode would have caught this.
You have another bug in the keys used to address items in storage:
'items' !== 'Item'
Also; you have no classes in your code.

knockout push values array

when I click on button1 I get object with 50 contacts array (containing collection of arrays with phoneNumbers, Addresses...), then when I click on button 2 I get the same object but my first object is erased whereas I would like to display 50 + 50 = 100 contacts array. I tried concat method but I have some difficulties to implement.
viewModel.initializeListener = function() {
$('#button1').click(function() {
document.getElementById("button2").style.visibility = "hidden";
$('#retrievedContactsDiv').html('');
nbDisplayedContacts = 0;
console.info("test");
viewModel.ui.FlashbackReport.MoreContacts();
});
$('#button2').click(function() {
viewModel.ui.FlashbackReport.MoreContacts();
console.info("test");
});
}; `
viewModel.WeHaveMoreContacts = function(data) {
console.info("test:", data)
if (viewModel.MoreContacts) {
var newArray=ko.mapping.fromJS(data, viewModel.MoreContacts);
var concatenated = newArray.concat(dataArray);
viewModel.MoreContacts.contacts(concatenated);
} else {
viewModel.MoreContacts = ko.mapping.fromJS(data);
var dataArray = viewModel.MoreContacts.contacts();
}
I have a parameter with number of contacts to skip for the server.
function which call the server then call the mapping function :
viewModel.ui.FlashbackReport.MoreContacts()
Problem : Object # has no method 'concat'
I made a fiddle that may help you.
The first part of the function generates new contacts and the second one add them to the existing contacts.
var VM = function () {
var self = this;
self.contacts = ko.observableArray();
self.addMore = function () {
// simulate server response
var offset = self.contacts().length;
var dataFromServer = [];
for (var index = 0; index < 10; index++) {
dataFromServer.push({
name: 'contact ' + offset + index
});
}
// add each new item to existing items.
ko.utils.arrayForEach(dataFromServer, function (item) {
self.contacts.push(item);
});
};
}
Feel free to ask more explanation.
I hope it helps.

IndexedDB handle data migration onupgradeneeded

i'm developing a offline-webapplication with IndexedDB. So I thought a lot about data migration in case of a version change.
For example, I had 3 ObjectStores in DB Version 3. Now i noticed, that i should have a specific index at all 3 ObjectStores. But its not possible to add an index afterwards to an existing ObjectStore, without losing data.
What could be the solution to handle data migration in an "onupgradeneeded"-event?
No Need to kill the StoreObject just update it like this :
request.onupgradeneeded = function(evt) {
var dataBase = evt.target.result;
var txn = evt.target.transaction;
//////////
var storeCreateIndex = function (objectStore, name, options) {
if (!objectStore.indexNames.contains(name)) {
objectStore.createIndex(name, name, options);
}
}
//////////
var catalogItem, mangaItem, chapterItem, artworkItem;
if (evt.newVersion != evt.oldVersion) {
// Get exiting objectStore
catalogItem = txn.objectStore('CatalogItem');
mangaItem = txn.objectStore('MangaItem');
chapterItem = txn.objectStore('ChapterItem');
artworkItem = txn.objectStore('ArtworkList');
} else {
// Fist creation of database objectStore
catalogItem = dataBase.db.createObjectStore("CatalogItem", { keyPath: "key" });
mangaItem = dataBase.db.createObjectStore("MangaItem", { keyPath: "key" });
chapterItem = dataBase.db.createObjectStore("ChapterItem", { keyPath: "key" });
artworkItem = dataBase.db.createObjectStore("ArtworkList", { keyPath: "key" });
}
//////////
storeCreateIndex(catalogItem, "popularity", { unique: false });
storeCreateIndex(catalogItem, "author", { unique: false });
storeCreateIndex(catalogItem, "status", { unique: false });
storeCreateIndex(catalogItem, "isFavorite", { unique: false });
storeCreateIndex(chapterItem, "isBookmarked", { unique: false });
storeCreateIndex(chapterItem, "isDownloaded", { unique: false });
}
As noted in the comments above:
Attempting to initiate a new transaction from "onupgradeneeded" will result the error:
InvalidStateError: DOM IDBDatabase Exception 11
Instead use the transaction referenced by the request object. Example:
function opendb(oncomplete){
var version = 1;
var migrateobjects = [];
var request = indexedDB.open('mydb', version);
request.onupgradeneeded = function(e) {
db = e.target.result;
transaction = e.target.transaction;
if(db.objectStoreNames.contains('myobjects')){
migraterequest = transaction.objectStore('myobjects').openCursor();
migraterequest.onsuccess = function(e){
var cursor = e.target.result;
if (cursor){
migrateobjects.push(cursor.value);
cursor.continue();
}
};
db.deleteObjectStore('myobjects');
}
var store = db.createObjectStore('myobjects', {keyPath: 'id'});
};
request.onsuccess = function(e) {
db = e.target.result;
transaction = db.transaction('myobjects', 'readwrite')
store = transaction.objectStore('myobjects');
for(var i=0; i < migrateobjects.length; ++i)
store.put(migrateobjects[i]);
transaction.oncomplete = oncomplete;
};
};
Changing index should not clear existing records unless new index violate database constraint of existing records. But I do observe object store blow away in Chrome but not in Firefox.

Categories