Is it possible to update an object's value within an IndexedDB index without cloning, deleting, or putting a new entry? Theoretically something like the following snippet would do the trick, though it probably would not delete until the put was confirmed. But it looks like overkill to me. It looks like it would be a nightmare to do any error handling on.
const objectStore = db.transaction([objectStoreName], 'readwrite')
.objectStore(objectStoreName);
const requestGet = objectStore.get(index);
requestGet.onsuccess = (event: any) => {
const value = event.target.result.value // Store old value
const requestDelete = objectStore.delete(index);
requestDelete.onsuccess = (event: any) => {
const requestPut = objectStore
.put({index: 'New Index Value', value: value}); // Put back using new index
};
};
You cannot directly change values in an object store's index. You can change the values of an object in an object store, and IndexedDB will propagate your changes to related indices. Indices are essentially read-only.
It is possible since you specify your index, otherwise, an other logic may be necessary.
As you should know, the IDBObjectStore has a method .put() which it will receive two params. With it you can either PUT a new value or UPDATE a value.
IDBObjectStore.put(item, key)
item: The item you want to put/update
key: opcional: Your primary object store key (such as an uuid, a random number, in short...) for that item you would like to update.
Code:
//This is an example only.
//Let's think that we have an object store into our IndexDB 'user', where object store is called by user-data:
//# Key Value
//0 1 { username: 'John Doe' }
//Here, we are receiving the 'success' result from an indexedDB.open(), and using its result with a promise.
dbPromise.then(db => {
//Getting the transaction
const transaction = db.transaction('user-data', 'readwrite')
//Getting the objectStore with the data, the same object store before.
const store = transaction.objectStore('user-data')
//Getting the key's object store, in the other other words, this is the key you define when you create you objectStore, with createObjectStore. In this example, I've used 'autoIncrement: true'
const query = store.get(1)
//Getting the query result with a success listener.
query.addEventListener('success', event => {
const { ['result']: user } = event.target
user.productsIntoCart.push(newItem)
//With this, we will be able to change the object store value.
user.username = 'Jane Doe'
store.put(user, 1)
})
query.addEventListener('error', event => console.error(event))
transaction.addEventListener('complete', () => db.close())
})
//# Key Value
//0 1 { username: 'Jane Doe' }
You can see more details you want in the MDN IDBObjectStore.put documentation.
IDBObjectStore
Related
I have a function that takes a string from a search field during runtime, and stores it to localstorage. Since we want to store all search strings from the end user to record it, we need to get the current data from localstorage, and add the latest search string.
Here is my code:
const setDatatoLocalStorag = (searchQuery: string) => {
let searchHistory = localStorage.getItem("searchHistory");
let searchQueryArr = [];
if (searchHistory) {
JSON.parse(searchHistory);
searchQueryArr.push(searchQuery, searchHistory);
} else {
searchQueryArr.push(searchQuery);
}
localStorage.setItem("searchHistory", JSON.stringify(searchQueryArr));
}
Lets assume we run the function twice, with the searchQuery "dog" and "cat". This is how it will look like in localstorage:
["cat","[\"dog\"]"]
I believe localstorage will get the item as string "[myData]" which will cause the error. How to properly handle this?
I have tried to follow How to store an array of objects in Local Storage? withous success.
The problem is you aren't assigning JSON.parse(searchHistory); to a variable. I think what you want to do is this:
var searchQueryArr = ['dog'];
localStorage.setItem("searchHistory", JSON.stringify(searchQueryArr));
var searchHistory = JSON.parse(localStorage.getItem("searchHistory") || '[]');
console.log(searchQueryArr, searchHistory);
Note I wasn't able to get this to work with the inline editor, but I did try it on a real server and it worked.
Make a setter/getter pair that hide the encoding/unencoding. Then add a higher-level push that does a get and a set...
// const pretend local storage, keys => strings
const myLocalStorage = {}
function mySetItem(key, value) {
// use the actual local storage setItem() here
myLocalStorage[key] = JSON.stringify(value);
}
function myGetItem(key) {
// use the actual local storage getItem() here
return JSON.parse(myLocalStorage[key]);
}
function myPush(key, value) {
let current = myGetItem(key);
current.push(value)
mySetItem(key, current)
}
// test
const key = 'myKey'
mySetItem(key, []);
myPush(key, { message: 'hello' })
myPush(key, { message: 'dolly' })
console.log(myGetItem(key))
I have an array contain unique keys, these keys I want to take it to get his Data from firebase real-time DB
the array like this
["dtnMG3dVEQXL1TPct3awl927jax2", "y5eAQppoEZRELsDL3hLbY5b0A763"]
so I iterate it using forEach Method to get a single key to check if found in the users tree or not But the result is null?
But when I Log the keys.forEach(key => console.log(key)) I can Get every key in a single line
firebase
.database()
.ref("users")
.child(keys.forEach(key => key))
.once("value")
.then(users => {
// let username = users.val().username;
console.log(users.val());
// Object.assign(users, { [Object.keys(users)]: username });
});
Edit
So thats fine and get every single username, Now the new issues is i want to save these names to other object i have
users = {
dtnMG3dVEQXL1TPct3awl927jax2: // the unique key
-LmUSeyWtBPqcoN8l6fd: [{…}]
-LmUgcbdzElxWpLkN-F9: [{…}]
// here's i want to add somthing like thies
-username : "userOne" //I got it from the Looping
y5eAQppoEZRELsDL3hLbY5b0A763:
-LmSTYlxa7bjy0Beazmy: [{…}]
-LmUad3lvkPdTQDFo4Ds: [{…}]
-LmUyDmGcEmYKOJnvEiA: [{…}]
// here's i want to add somthing like thies
-username : "userTwo" // I got it from the Looping
}
keys.forEach(key => key)
This return every element of the array keys. And you are passing these elements to the child function. However, child functions expects only one argument.
Try this:
for( let key of keys){
firebase
.database()
.ref("users")
.child(key)
.once("value")
.then(users => {
// let username = users.val().username;
console.log(users.val());
// Object.assign(users, { [Object.keys(users)]: username });
});
}
Side not: Do not call async function inside forEach loop.
.child(keys.forEach(key => key)) - This is your problem. This forEach effectively returns all your array values one by one. But you call it inside a function with one argument that gets called one time. So it will only check for the first value in your array and the rest will be dumped, so its like looking inside a 1-element array.
Solution is to perform the DB calls inside the iteration:
keys.forEach( key => {
firebase
.database()
.ref("users")
.child(key)
.once("value")
.then(users => {
// let username = users.val().username;
console.log(users.val());
// Object.assign(users, { [Object.keys(users)]: username });
}});
EDIT:
to do what you wanted after you edited, assuming the variable curUser holds the username you want to insert and curKey is the key associated to that username, just: users[key]["username"] = curUser;. This will reference the property which is named after curKey in the users object, and will create a "username" property in it and store the value there.
Notice that if you already have "username" property in this key object it will be overriden.
** I assumed the data structure of the key-properties inside "users" are objects even tho your code doen't really show it.
I'm trying to pass a property, that is inside the first position of an array of objects, to another module so I can use this value later. I've tried to pass it as module(args), but it keeps reading the default value which is 0. Is there a way to do this?
I tried to implement some React.context but the Bot framework Emulator is refusing it.
/////////////////Module that ll acquire the value/////////////////////////////
getCard(bot, builder, params) {
let configValues = { ...params[0] }
bot.dialog(`${configValues.path}`, function (session) {
var msg = new builder.Message(session);
const cardItem = (obj) => {
return (new builder.HeroCard(session)
.title(`${obj.title}`)
.text(`R$ ${obj.price}`)
.images([builder.CardImage.create(session, `${obj.img}`)])
.buttons([
builder.CardAction.imBack(session, `${obj.price} Item adicionado!`, 'add to cart')
// !onClick event must add the current obj.price to
// the configValues.total(Ex: configValues.total += obj.price)!
])
)
}
msg.attachmentLayout(builder.AttachmentLayout.carousel)
msg.attachments(
eval(params.map(obj => cardItem(obj)))
);
//!in here before end the dialog is where i want to update
// the configValues.total so i can show it in the -> Checkout module
session.send(msg).endDialog()
}).triggerAction({ matches: configValues.regex });
}
}
//////////////CheckOut.Module///////////////////////////////
{...}
let configValues = { ...params[0] }
let state = {
nome: "",
endereco: "",
pagamento: "",
total: configValues.total // this is the value to be read
}
bot.dialog('/intent', [
{...},
(session, results) => {
state.pagamento = results.response
session.send(
JSON.stringify(state) // here is the place to be printed
)
{...}
]
).triggerAction({ matches: /^(finalizar|checar|encerrar|confirmar pedido|terminar)/i })
Since you solved your original problem, I'll answer the one in your comment.
Your problem is here:
cartId.map((obj, i , arr) => {
// if (!obj.total) {
// obj.total.reduce(i => i += i)
// }
const newtotal = new total
newtotal.getTotals(bot, builder, obj, arr)
})
cartId contains the totals for each of your items. When you call map on it, you're passing each item individually to getTotals, which passes each item to checkout()
The reason you can't sum all of the totals and can only sum one item's total is that you pass cartId to checkout and cartId has been changed to just a single item. Instead, there's a couple of different things you could do:
Pass the whole cartId from cartItems and use something like for (var key in cartItems) in totalConstructor() and checkoutConstructor(). This is probably the easiest, but not very memory efficient.
Use BotBuilder's State Storage to store your totals array in userData, then sum that at the end. This might be more difficult to implement, but would be a much better route to go. Here's a sample that can help you get started.
//It's working now - updated code
I'm working on my own autocomplete component because I have problem with passing firebase data to a ready one.
The whole mechanism is working good but I have problem with passing values after getting user input
I'm setting initial state with those values
const INITIAL_STATE = {
allChars: [],
suggestions: [],
value: ""
};
Then in autocomplete class i'm loading all users from database
loadData(){
let self = this;
let characters = firebase.firestore().collection("users");
characters.get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
let document = doc.data();
self.setState(({allChars})=>({
allChars: [
...allChars,
document
]
}))
});
});
}
Here is my getSuggestions function. It is firing on input change
getSuggestions = event => {
const {value, suggestions} = event.target;
this.setState({
value: value,
suggestions: []
})
let suggest = [];
this.state.allChars.map((allChars) => {
if(value.length > 1 && allChars.name.toLowerCase().includes(value.toLowerCase())){
suggest.push (
allChars.name
);
}
})
this.setState({
suggestions: suggest
})
}
In render I just put {sugestions}
But in {suggestions} I get rendered only one name.
one
But when I console.log it - I get two names
two
There should be two.
I tried to set state in this function like in loadData(), but I still get only one value.
Is there other way to get both values into DOM
Full code can be found here: https://github.com/Ilierette/react-planner/blob/master/src/component/elements/Autocomplete.js
I think the reason you are just seeing one element each time your components re-render is that in your map function on your allChars array, when you want to update the suggestions in your state, you are setting just the name each time as a new array while you should update the existing array in your state, so your code should be:
this.setState({
suggestions: [...this.state.suggestions, allChars.name]
})
I am working with React 16.3.2, Redux 4 and Dexie 2.0.3.
when I am going to store data second time it throws this error message.
Error: ConstraintError: Key already exists in the object store.
return dispatch => {
db.table
.add(data)
.then (function(id){
console.log(id)
})
.catch (function (error) {
console.log("Error: " + error);
});
}
My Db schema:
const db = new Dexie('ReactReduxDexieJsCRUD');
db.version(1).stores({table:'++id,name,age,bloodGroup,donateBefore,weight' });
The first time it stores date well but after it gives the error.
How does your schema look like? (the part db.version(x).stores({...}) ?
The most common is to have inbound primary key, example:
db.version(1).stores({
table: 'id, idx1, idx2...'
});
Here id is the primary key.
db.table.add({id: 1, foo: 'bar'}) will add object with id 1.
db.table.add({id: 1, foo: 'bar2'}) 2nd time will fail because id 1 exists.
db.table.put({id: 1, foo: 'bar2'}) will update object with id 1.
So what do you really want to do? You say you want to add new object with new key. If so, I suppose the error is that you give the same key second time.
You can also let the id be generated by the db
db.version(2).stores({
table: '++id, idx1, idx2...'
});
Then you don't need to supply id in calls to add():
db.table.add({foo: 'bar'}) will add object with id 1.
db.table.add({foo: 'barX'}) 2nd time will add new obj with id 2
...