I've been learning JavaScript for about a month now, and right now I’m working on a little project (Shopping Cart) to dive into storage events. I’ve successfully managed to store a few keys from “tab1” into the local storage and retrieve them from “tab2”. However, I came across a particular “problem”, which I’ve been struggling with for the last couple of days.
On “tab1” I have five keys that are being stored in localStorage. on “tab2” I need to complete an action (create an HTML element) every time the localStorage changes. The problem is that the action is being triggered once for every key that’s changed. In other words, I keep getting 5 duplicate HTML elements.
I’ve spent many hours searching for answers on forums, YouTube videos, blogs and of course here. So far no luck. I’ve also been reading the documentation on localStorage, but since I’m new at this, it’s not very clear for me.
I hope you can help find a solution or understand why I keep getting these duplicate actions.
This is an example of the code I have so far:
let itemList = document.getElementById("itemList");
let summaryItem = document.getElementById("summaryItem_01");
let summaryImage = document.getElementById("itemImage_01");
let summaryName = document.getElementById("item_Name_01");
let summaryModel = document.getElementById("itemModel_01");
let summaryQuantity = document.getElementById("detailQuantityDisplay_01");
let summaryPrice = document.getElementById("itemPriceAmount_01");
//Gets localStorage info on page load and feeds summaryItem fields.
window.addEventListener("load", () => {
let itemImage = localStorage.getItem("modalItemImage");
let itemName = localStorage.getItem("modalItemName");
let itemModel = localStorage.getItem("modalItemModel");
let itemQuantity = localStorage.getItem("modalItemQuantity");
let itemPrice = localStorage.getItem("modalItemUnitPrice");
//Prints localStorage info to summaryItem element.
summaryImage.setAttribute("src", itemImage);
summaryName.innerText = itemName;
summaryModel.innerText = itemModel;
summaryQuantity.value = itemQuantity;
summaryPrice.innerText = itemPrice;
});
//This is where I'm getting the duplicate action
window.addEventListener("storage", () => {
let a = document.createElement("article");
itemList.appendChild(a);
});
UPDATE:
After MauriceNino's suggestion, I ended up with this code and it worked perfectly:
//Tab1
let modalItem = {
modalItemImage: displayModalImage.innerHTML.slice(10, -2),
modalItemName: displayModalName.innerText,
modalItemModel: displayModalModel.innerText,
modalItemQuantity: displayModalQty.value,
modalItemUnitPrice: displayModalPrice.innerText,
modalItemTotal: displayModalTotal.innerText,
};
localStorage.setItem("modalItem", JSON.stringify(modalItem));
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//Tab2
//Gets localStorage info from modal
let data = JSON.parse(localStorage.getItem("modalItem"));
let dataArray = Object.values(data);
let itemImage = dataArray[0];
let itemName = dataArray[1];
let itemModel = dataArray[2];
let itemQuantity = dataArray[3];
let itemPrice = dataArray[4];
//Prints localStorage info to summaryItem element.
summaryImage.setAttribute("src", itemImage);
summaryName.innerText = itemName;
summaryModel.innerText = itemModel;
summaryQuantity.value = itemQuantity;
summaryPrice.innerText = itemPrice;
//Creates new summaryItem when there's a chnage on localStorage.
window.addEventListener("storage", () => {
let a = document.createElement("article");
itemList.appendChild(a);
});
You could just save it as one element like so:
//Gets localStorage info on page load and feeds summaryItem fields.
window.addEventListener("load", () => {
// Get all the data in a single statement
let { itemImage, itemName, itemModel, itemQuantity, itemPrice }
= localStorage.getItem("modalItem");
//Prints localStorage info to summaryItem element.
summaryImage.setAttribute("src", itemImage);
// ...
});
//Will be called only once now
window.addEventListener("storage", () => {
let a = document.createElement("article");
itemList.appendChild(a);
});
// This is where you are saving your localStorage settings
localStorage.setItem("modalItem", {
itemImage: 'image',
itemName: 'name',
itemModel: 'model',
itemQuantity: 2,
itemPrice: 3
});
Related
i know that the problem is that let todoList is an empty array, but i dont know how to solve it.
the id tags in my created html is so e can create a delete button later
heres my code:
const textArea = document.querySelector("textarea");
const button = document.querySelector("button");
const listContainer = document.querySelector(".list-container");
let id = 0;
let todoList = [];
button.onclick = function () {
const listItem = {
title: textArea.value,
};
todoList.push(listItem);
addToStorage(todoList);
const dataFromStorage = getFromStorage();
createHtml(dataFromStorage);
};
function addToStorage(items) {
const stringify = JSON.stringify(items);
localStorage.setItem("list", stringify);
}
function getFromStorage() {
const data = localStorage.getItem("list");
const unstrigified = JSON.parse(data);
return unstrigified;
}
const createHtml = (data) => {
id++;
listContainer.innerHTML = "";
data.forEach((item) => {
listContainer.innerHTML += `<div class="list-item" data-id=${id}><p>${item.title} </p><button class="remove" data-id=${id}>Delete</button></div>`;
});
};
The problem here is you just forgot to load the data from localStorage when the page loaded like this
window.onLoad = () => {
const dataFromStorage = getFromStorage();
if(dataFromStorage){
createHtml(dataFromStorage);
} else {
createHtml([]);
}
}
The problem in the code is as follows
Initially the todolist will be an empty array. so when you do the below
todoList.push(listItem);
// adding to local storage which will override the existing todos when page is refreshed
addToStorage(todoList);
// So when the below line is executed only the latest todo will be returned
const dataFromStorage = getFromStorage();
createHtml(dataFromStorage);
Fix:
Initialise the todos from localstorage instead of an empty array
let todoList = [];
// change it as below
let todoList = getFromStorage();
Now Modify the getFromStorage() as below
// If the data is present in the localStorage then return it, else return empty array
function getFromStorage() {
const data = localStorage.getItem("list");
if (!data) return [];
const unstrigified = JSON.parse(data);
return unstrigified;
}
Now when the page is loaded, we need to display the todos. Add the below lines of code
window.onload = function () {
createHtml(todoList);
};
That's it. This will fix the issue.
Few minor improvements can be made as well.
todoList.push(listItem);
addToStorage(todoList);
const dataFromStorage = getFromStorage(); // this line is not necessary, remove it
createHtml(dataFromStorage); // change this to createHtml(todoList)
Codepen
Thanks.
I've already asked that question but my explanation was pretty bad, so I decided to ask again with a better explanation and with actual code (I'll ask moderators to delete one of the posts). So let's consider the problem.
Following snippet represents rendering notes from array. However, during the adding note part, I mutate a state. So the question is: how can I add a new note in notes array without mutating? In other words, I want to remove replaceNotes and remain the same functionality. I know that it's possible to add notes without array at all, but I do need to update array with notes in due to the future reference. The ting is, in my original application I've got lists with notes, and while I switch between lists, I should get rendered notes that relies to the list I switch on. That's why I should keep the reference to notes array.
At the same time I'm wondering, would it be okay, if I just store notes in localStorage and then take notes from that data? Is it a good practice in functional programming?
const button = document.getElementById('button');
const notesContainer = document.querySelector('.notes');
const pipe = (f, g) => (...args) => f(g(...args));
let notes = [];
const createNote = (...fns) => fns.reduceRight(pipe);
const handleEvent = () =>
createNote(gatherContent, renderContent, replaceNotes)(notes);
function gatherContent(notes) {
const name = prompt('How do you want to name a note?');
return [...notes, { name }];
}
function renderContent(notes) {
function render(note) {
const noteEl = document.createElement('div');
noteEl.innerHTML = `<p>${note.name}</p>`;
notesContainer.append(noteEl);
}
notesContainer.innerHTML = '';
notes.map(render);
return notes;
}
const replaceNotes = newNotes => (notes = newNotes);
button.addEventListener('click', handleEvent);
<button id="button">Click me!</button>
<section class="notes"></section>
Here is how to create a simple task list app without mutating anything except for the DOM.
const button = document.getElementById("button");
const section = document.getElementById("notes");
const template = document.getElementById("template");
template.parentNode.removeChild(template);
const render = notes => {
button.onclick = event => {
const name = prompt("How do you want to name a note?");
render([...notes, { name }]);
};
while (section.lastChild) {
section.removeChild(section.lastChild);
}
for (const note of notes) {
const node = template.cloneNode(true);
node.firstChild.firstChild.nodeValue = note.name;
section.appendChild(node);
}
};
render([]);
<button id="button">Click me!</button>
<section id="notes"></section>
<div id="template"><p>name</p></div>
For a detailed explanation, read my previous answer. https://stackoverflow.com/a/58642199/783743
You can use this pattern with localStorage too.
const button = document.getElementById("button");
const section = document.getElementById("notes");
const template = document.getElementById("template");
template.parentNode.removeChild(template);
const render = notes => {
localStorage.setItem("notes", notes); // set notes
button.onclick = event => {
const name = prompt("How do you want to name a note?");
render([...notes, { name }]);
};
while (section.lastChild) {
section.removeChild(section.lastChild);
}
for (const note of notes) {
const node = template.cloneNode(true);
node.firstChild.firstChild.nodeValue = note.name;
section.appendChild(node);
}
};
render(localStorage.getItem("notes") || []); // get notes
Note that localStorage should only be used to save state that you want to use across sessions. It's not recommended to use localStorage as your application store. That would result in both bad performance and bad code structure.
Question: How do I store a large array full of objects, all of which have 5 properties and all except the id property must be updated. Further more, why won't the code below work and how can I format it to work with the main question?
Info I've viewed:
https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/openCursor
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/createIndex
Note: I am aware of the setInterval and its inefficiency, it is for testing purposes so I do not have to click many times to check for a result.
<html>
<body>
<script type="text/javascript">
let count =0;
let storeBuilt = false;
const dbName = "the_name";
let version=82;
let storeName= "store82";
let storeBuilding= false;
setInterval(build,1000/24);
function build(){
hello()
}
function hello(){
let customerData = [];
for(let i=0;i<=50000;i++){
customerData.push({name:"bob",minX:random(),minY:random(),maxX:random(),maxY:random(),id:random()})
}
let request = indexedDB.open(dbName, version);
request.onsuccess= function(event){
let db = event.target.result;
let transaction = db.transaction( storeName,"readwrite").objectStore(storeName);
if( storeBuilding=== false&& storeBuilt=== false){
storeBuilding= true;
let additem = addData(customerData, transaction);
additem.onsuccess= function(e){storeBuilt=true}
} else if (storeBuilt=== true){
let updateitem= updateData(customerData, transaction);
}
};
request.onupgradeneeded = function(event) {
let 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.
let objectStore = db.createObjectStore(storeName, {keyPath:"names",autoIncrement:true});
objectStore.createIndex("name","name",{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.
let customerObjectStore = db.transaction(storeName, "readwrite").objectStore(storeName);
}
};}
function random (){
return (Math.floor((Math.random() * 10) + 1))
}
function addData(data,transaction){
return transaction.add(data)
}
function updateData(data,transaction){
let openCursor = transaction.index("name").openCursor();
openCursor.onsuccess= function(event){
let cursor = event.target.result;
if (cursor){
alert (cursor);
for(let I in data){
let item = data[I];
if(item.id === cursor.value.id){
let updateProperty = cursor.value;
updateProperty.minX = item.minX;
cursor.update(updateProperty);
cursor.continue()
}
}
}{alert("none")}
}
}
function deleteData(data,transaction){
}
</script>
</body>
</html>
Not sure if I understand the problem clearly, but generally you will want to load the objects from the object store, modify each object's properties, and then store the objects in the object store. There are several ways to do this. One way is to use cursor.update, but I don't think you need to do this at all. Just overwrite the objects.
function storeThings(db, things, callback) {
var txn = db.transaction('store82', 'readwrite');
txn.oncomplete = callback;
var store = txn.objectStore('store82');
for(var thing of things) {
store.put(thing);
}
}
function open(callback) {
var request = indexedDB.open();
request.onsuccess = _ => callback(request.result);
}
var things = [{id:1}, {id:2}, {id:3}];
open(db => storeThings(db, things, _ => console.log('done')));
I am using IDBObjectStore.prototype.put to store the objects. The put method will either create or overwrite an object in the store. It will create a new object in the store when no matching object is found based on the keypath. It will replace an existing object in the store when a matching object is found.
In your case, you are using ssn string as a keypath. So, in other words, it will create new people if ssn not found, or overwrite people if ssn found. You just need to make sure that the ssn property is defined within each person object you pass to put, or indexedDB will complain.
Hoping someone out there could tell me where I am going wrong with this update method:
changeTaskDetails: function(singleID,detailsTarget){
TaskDetails.update({
_id: singleID,
}, {
$set:{
projectType: detailsTarget,
}
});
console.log(singleID);
},
Here is the event:
'submit #editTaskDetails'(event){
event.preventDefault();
var id = FlowRouter.getParam('taskId');
const singleDetailsUpdate = Tasks.findOne({_id:id});
const singleID = singleDetailsUpdate._id;
const target = event.target;
const facilityTarget = target.facilityName.value;
const inspectorTargetName = target.detailsinspector.value;
const inspectorIdTarget = target.inspectorid.value;
const detailsTarget = target.detailstype.value;
const dateTarget = target.TaskDate.value;
console.log(singleID)
Meteor.call("changeTaskDetails", singleID,detailsTarget);
},
I can get the 2 props to log...but its not updating the DB. No errors in either console.
I figured it out! I had a session variable controlling when you could (or could not) see the update form. For one reason or another the form crapped out when inside of this area. When I removed the session, and added the form to the main template...it worked!
I am reading a lot of different data from my firebase database, currently, I have hard coded it. This works fine, however I have soo many lines of code that now when I want to alter my code it gets really confusing. Below I have pasted the current apporach I have taken.
var ref = new Firebase("URL");
// Data set 1
ref.on('child_added', function(snapshot) {
var snapshot = snapshot.val();
textbox1.innerHTML = snapshot.getvalue1.age;
});
ref.on('child_changed', function(snapshot) {
var snapshot = snapshot.val();
textbox1.innerHTML = snapshot.getvalue1.age;
});
// Data set 2
ref.on('child_added', function(snapshot) {
var snapshot = snapshot.val();
textbox2.innerHTML = snapshot.getvalue2.age;
});
ref.on('child_changed', function(snapshot) {
var snapshot = snapshot.val();
textbox2.innerHTML = snapshot.getvalue2.age;
});
.....
.....
.....
// Data set 100
ref.on('child_added', function(snapshot) {
var snapshot = snapshot.val();
textbox100.innerHTML = snapshot.getvalue100.age;
});
ref.on('child_changed', function(snapshot) {
var snapshot = snapshot.val();
textbox100.innerHTML = snapshot.getvalue100.age;
});
Instead of the approach I have taken, is it possible to use a for loop or something like that to loop through each data because my structure for each textbox / keyword in firebase is more or less the same.
I am fairly new to javascript but from my knowledge of java, I believe it would be started of something like this;
var myTextbox = document.getElementById("mytextbox");
for (var i = 0; i < myTextbox.length; i++) {
}
Any help is welcomed, if my question is not clear please let me know.
EDITED:
Mydata:
textbox1 - value - age : "This is textbox 1, age:21"
textbox2 - value - age : "This is textbox 2, age:53"
textbox2 - value - age : "This is textbox 3, age:04"
....
....
I am not an expert on firebase but here are some potential solutions you can try. For example, instead of writing a child_added and child_changed, you can use 'value'. (Reference)
ref.on('value', function(snapshot) {
var snapshot = snapshot.val();
textbox1.innerHTML = snapshot.getvalue1.age;
});
But this is not a good solution for your problem as you want all the values retrieved at once.
It seems your snapshot has all the values with attributes'getValuei' where i is from 1...n.
A better solution could be something like this..
ref.on('value', function(snapshot) {
var snapshot = snapshot.val();
textbox1.innerHTML = snapshot.getvalue1.age;
textbox2.innerHTML = snapshot.getvalue2.age;
textbox3.innerHTML = snapshot.getvalue3.age; //..and so on..
});