I have blobs in my Vuex Store, and I want to download the state of my application as.JSON and restore it.
In general, JSON.stringify(store.state,...) works great without blobs. I just don't know how I can store the Blobs into JSON.
Here is my code. The if(value instanceof Blob)-Block contains my attempt to convert the BLOB into JSON.
const s = JSON.stringify(store.state
, (key, value) => {
// let process = true;
if (typeof value === 'object') {
if(value instanceof Blob){
//I can't get this to work:
// const reader = new FileReader();
// // let process = false;
// reader.onload = function(event){
// console.log(JSON.stringify(reader.result));
// value = JSON.stringify(reader.result);
// return value;
// };
// reader.readAsText(value);
// process = false;
}
// Duplicate reference found, discard key
if (cache.includes(value)) return;
// Store value in our collection
cache.push(value);
}
if(process){
return value;
}
})
//download
const newBlob = new Blob([s], { type: 'application/json;' });
const filename = `jam-along-${new Date().getTime()}.json`;
saveAs(newBlob, filename);
cache = [];
}
Another idea was to use Vuex-Persist to download the store with a customized saveState: (key, state, storage) =>{..., but I can't get it to work. I don't want vuex-persist to do anything in the sessionStore or the localStorage, and I want to trigger saveState from outside without mutating the store.
Is there any way to download and restore my vuex store?
Thank you!
Related
I'm working on a simple to-do list with vanilla js. I've managed to add the input to local storage, but have not been able to add the style changes(check strike through) to local storage, nor can I figure out how to remove one item at a time from storage. I have been able to clear all, just unable to remove each item separately. Below is my code, any advice is greatly appreciated.
//local storage setup
let saved = window.localStorage.getItem(input.value);
if (saved) {
list.innerHTML = saved;
}
//handle input submit
function handleSubmitForm(e) {
e.preventDefault();
let input = document.querySelector('input');
if (input.value != '') {
addTodo(input.value);
}
input.value = '';
window.localStorage.setItem(input.value, list.innerHTML);
}
//check off todo
function checkTodo(e) {
let item = e.target.parentNode;
if (item.style.textDecoration == 'line-through') {
item.style.textDecoration = 'none';
} else {
item.style.textDecoration = 'line-through';
}
window.localStorage.setItem(item);
}
//delete todo
function deleteTodo(e) {
let item = e.target.parentNode;
item.addEventListener('transitionend', function () {
item.remove();
});
item.classList.add('todo-list-item-fall');
window.localStorage.removeItem(item);
}
JavaScript Storage is a key-value pair. Just use a string-based key so you can remove, edit or read it easily.
// Set todo item
localStorage.setItem("todo1", "Stand-up meeting 9.15am");
// Read todo item
localStorage.getItem("todo1");
// Delete todo item
localStorage.removeItem("todo1");
It's better if you can save it as a JSON string because you can mark it as completed without delete, so you can find completed tasks too.
// Saving todo item as a JSON string
localStorage.setItem("todo1", JSON.stringify({ text: "Stand-up meeting 9.15am", completed: false }));
// Read it
const todo = JSON.parse(localStorage.getItem("todo1"));
// You can read the text
console.log(todo.text);
// Also you can mark it as completed and save it back
todo.completed = true;
localStorage.setItem("todo1", JSON.stringify(todo));
Storing object in localStorage is a tricky job.
Everything you store in the local or session storage is of type string
you can create an object like
item = {
value : ANY_VALUE
}
and save it in your localStorage using JSON.stringify
localStorage.setItem(`item`,JSON.stringify(item))
now when you want to update the item just update the object and again set using the ablove syntax
To access the saved item from the local storage use JSON.parse
yourItemObject = JSON.parse(localStorage.getItem())```
You can access values now using yourItemObject .value
It appears you're passing the whole HTML element (it passed as an object) inside the removeItem function. you need to pass the key instead.
try localStorage.removeItem(item.innerText);
If you are working with lists in localStorage. I would use something like this basic example:
function addTodo(key, item){
var list = getTodo(key);
list.push(item);
localStorage.setItem(key, JSON.stringify(list) );
}
function getTodo(key){
try{
var rawList = localStorage.getItem(key);
return JSON.parse(rawList) || [];
}
catch(e){
return [];
}
}
function removeTodo(key, id){
var list = getTodo(key);
var newlist = list.filter( function(item){
return item.id != id;
});
localStorage.setItem(key, JSON.stringify(newlist) )
}
function emptyTodo(key){
localStorage.removeItem(key);
}
addTodo('list', {
id: 1,
text: 'do shopping'
});
addTodo('list', {
id: 2,
text: 'study'
});
console.log( getTodo('list') );
removeTodo('list', 1);
console.log( getTodo('list') )
emptyTodo('list');
So I have been trying to make a system with "memory". I used a JSON file for this, but it never refreshes the file. I looked it up and I got a function showing this
function requireUncached(module) {
delete require.cache[require.resolve(module)];
return require(module);
}
but they didn't show any syntax. Do I only put it at the top instead of const whatever = require(file)? Do I do it in every function it needs to be refreshed in? I have no idea. The reason I need this is so that it is all automatic and I don't have to do node . every time.
Warning, using Proxy and fs.writeFileSync() is slow
Here's a convenient function that uses a Proxy to automatically write changes back to disk whenever the object in memory is updated:
const fs = require('fs');
const storage = (path, encoding = 'utf8', space = null) => {
const object = JSON.parse(fs.readFileSync(path, encoding));
let immediate = null;
const scheduleWriteFile = () => {
clearImmediate(immediate);
immediate = setImmediate(() => {
fs.writeFileSync(path, JSON.stringify(object, null, space), encoding);
});
};
const handler = {
get(target, property) {
const value = Reflect.get(target, property);
if (Object(value) === value) {
const descriptor = Reflect.getOwnPropertyDescriptor(target, property);
if (descriptor === undefined || descriptor.configurable || descriptor.writable) {
return new Proxy(value, handler);
}
}
return value;
},
...Object.fromEntries(['defineProperty', 'deleteProperty', 'set'].map(
(key) => [key, (...args) => {
const result = Reflect[key](...args);
if (result) {
scheduleWriteFile();
}
return result;
}]
))
};
return new Proxy(object, handler);
};
Example usage:
const settings = storage('config.json', 'utf8', 2);
...
// automatically schedules call to
// fs.writeFileSync('config.json', JSON.stringify(settings, null, 2), 'utf8')
// after updating object
settings.users[user.id].banned = true;
The advantage of using setImmediate() is that if multiple properties are updated synchronously on settings, it only schedules one call to fs.writeFileSync(), and it will occur after the event loop processes currently pending I/O events.
Because the proxy object recurses, you can treat settings exactly as a normal object, keep variable references to object or array properties, and read primitive values from it as usual.
The only restriction is that the JSON file must begin with { or [ in order for the object to be allowed as the target of a Proxy.
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))
I want to execute this query
select * from properties where propertyCode IN ("field1", "field2", "field3")
How can I achieve this in IndexedDB
I tried this thing
getData : function (indexName, params, objectStoreName) {
var defer = $q.defer(),
db, transaction, index, cursorRequest, request, objectStore, resultSet, dataList = [];
request = indexedDB.open('test');
request.onsuccess = function (event) {
db = request.result;
transaction = db.transaction(objectStoreName);
objectStore = transaction.objectStore(objectStoreName);
index = objectStore.index(indexName);
cursorRequest = index.openCursor(IDBKeyRange.only(params));
cursorRequest.onsuccess = function () {
resultSet = cursorRequest.result;
if(resultSet){
dataList.push(resultSet.value);
resultSet.continue();
}
else{
console.log(dataList);
defer.resolve(dataList);
}
};
cursorRequest.onerror = function (event) {
console.log('Error while opening cursor');
}
}
request.onerror = function (event) {
console.log('Not able to get access to DB in executeQuery');
}
return defer.promise;
But didn't worked. I tried google but couldn't find exact answer.
If you consider that IN is essentially equivalent to field1 == propertyCode OR field2 == propertyCode, then you could say that IN is just another way of using OR.
IndexedDB cannot do OR (unions) from a single request.
Generally, your only recourse is to do separate requests, then merge them in memory. Generally, this will not have great performance. If you are dealing with a lot of objects, you might want to consider giving up altogether on this approach and thinking of how to avoid such an approach.
Another approach is to iterate over all objects in memory, and then filter those that don't meet your conditions. Again, terrible performance.
Here is a gimmicky hack that might give you decent performance, but it requires some extra work and a tiny bit of storage overhead:
Store an extra field in your objects. For example, plan to use a property named hasPropertyCodeX.
Whenever any of the 3 properties are true (has the right code), set the field (as in, just make it a property of the object, its value is irrelevant).
When none of the 3 properties are true, delete the property from the object.
Whenever the object is modified, update the derived property (set or unset it as appropriate).
Create an index on this derived property in indexedDB.
Open a cursor over the index. Only objects with a property present will appear in the cursor results.
Example for 3rd approach
var request = indexedDB.open(...);
request.onupgradeneeded = upgrade;
function upgrade(event) {
var db = event.target.result;
var store = db.createObjectStore('store', ...);
// Create another index for the special property
var index = store.createIndex('hasPropCodeX', 'hasPropCodeX');
}
function putThing(db, thing) {
// Before storing the thing, secretly update the hasPropCodeX value
// which is derived from the thing's other properties
if(thing.field1 === 'propCode' || thing.field2 === 'propCode' ||
thing.field3 === 'propCode') {
thing.hasPropCodeX = 1;
} else {
delete thing.hasPropCodeX;
}
var tx = db.transaction('store', 'readwrite');
var store = tx.objectStore('store');
store.put(thing);
}
function getThingsWherePropCodeXInAnyof3Fields(db, callback) {
var things = [];
var tx = db.transaction('store');
var store = tx.objectStore('store');
var index = store.index('hasPropCodeX');
var request = index.openCursor();
request.onsuccess = function(event) {
var cursor = event.target.result;
if(cursor) {
var thing = cursor.value;
things.push(thing);
cursor.continue();
} else {
callback(things);
}
};
request.onerror = function(event) {
console.error(event.target.error);
callback(things);
};
}
// Now that you have an api, here is some example calling code
// Not bothering to promisify it
function getData() {
var request = indexedDB.open(...);
request.onsuccess = function(event) {
var db = event.target.result;
getThingsWherePropCodeXInAnyof3Fields(db, function(things) {
console.log('Got %s things', things.length);
for(let thing of things) {
console.log('Thing', thing);
}
});
};
}
For example:
object1(1) = {
name: 'Rhodok Sergeant',
speciality: 'Hand to hand battle'
}
then I want to update only the speciality field, into:
object1(1) = {
name: 'Rhodok Sergeant',
speciality: 'Long range battle'
}
Thank you.
This is possible using following steps -
fetch the item first using idbcursor
update that item
call cursor.update to store updated data in indexedb.
An example code is -
const transaction = db.transaction(['rushAlbumList'], 'readwrite');
const objectStore = transaction.objectStore('rushAlbumList');
objectStore.openCursor().onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
if (cursor.value.albumTitle === 'A farewell to kings') {
const updateData = cursor.value;
updateData.year = 2050;
const request = cursor.update(updateData);
request.onsuccess = function() {
console.log('data updated');
};
};
cursor.continue();
}
};
Check out this link for more info - https://developer.mozilla.org/en-US/docs/Web/API/IDBCursor/update
Note:- In above code, i am looping through all records which is not efficient in case you want to fetch the particular record based on some condition. So you can use idbKeyRange or some other idb query alternative for this.
you cannot do partial updates, you can only overwrite an entire object
read the object in from memory, change it, and then write it back