Delete Session Storage - Shopping Cart Project - javascript

I hope this is a good question. I am working on a shopping cart project. I have been scouring the internet through different tutorials on shopping carts. I am attempting to write mine in Vanilla Javascript. I am having a problem with removing shopping cart items from session storage.
Below is what is currently in my session storage. As you can see it is an Array of objects.
[{"id":"8","name":"Candy
Skull","price":"20000","image":"../images/candyskull-
min.JPG","qty":"1"},{"id":"5","name":"Upsidedown
House","price":"20000","image":"../images/upsidedownhouse-
min.JPG","qty":"1"},{"id":"6","name":"Brooklyn
Window","price":"30000","image":"../images/brooklynwindow-
min.JPG","qty":"1"},{"id":"4","name":"Hand That
Feeds","price":"40000","image":"../images/handthatfeeds-
min.JPG","qty":"1"}]
I want to loop through the array and remove the matching object from the cart storage.
Below is the JS Code used to generate the .remove-from-cart buttons. As you can see it includes all the dataset information.
<td>
<span class="remove-from-cart">
<b data-id="${value.id}" data-name="${value.name}" data-
price="${value.price}" data-image="${value.image}" data-
qty="${value.qty}">X</b>
</span>
</td>
To test the functionality of what I have done so far you can visit www.dancruzstudio.com/shop
The function that I can't get to work properly is the removeFromStorage() function. For some reason when comparing an object to the objects in the array I'm never getting back a true boolean value, even when there are items in the cart that should match. Where am I going wrong? I hope someone can help. Below is a copy of my JS code.
The method I am using is having an identical dataset value in the remove item button generated by JS and then parsing that dataset into an object and comparing it to the objects in the session storage array which is called shopItems inside the removeFromStorage() function. I hope this information is enough for someone to see my problem. Thank you in advance.
// Remove item from DOM
document.querySelector('#cart-list').addEventListener('click',
removeFromCart)
function removeFromCart(e) {
if(e.target.parentElement.classList.contains('remove-from-cart')) {
//Remove from DOM
e.target.parentElement.parentElement.parentElement.remove();
//Remove from Session Storage
removeFromStorage(e.target.dataset);
}
}
// remove from Session storage
function removeFromStorage(removedItem){
let shopItems;
if(sessionStorage['sc'] == null){
shopItems = [];
} else {
shopItems = JSON.parse(sessionStorage['sc'].toString());
}
var compare = JSON.parse(JSON.stringify(removedItem))
shopItems.forEach(function(item, index){
if(compare === item){
console.log(compare);
console.log(item);
// shopItems.splice(index, 1);
}
});
sessionStorage['sc'] = JSON.stringify(shopItems);
}

You can not compare objects like this.
let a = {p:1};
let b = {p:1};
console.log(`a ===b ? ${a===b}`);
If your objects are fairly simple you can try comparing their stringify representation:
let a = {p:1};
let b = {p:1};
const compare = (x,y) => {
return JSON.stringify(x) === JSON.stringify(y);
}
console.log(`a === b ? ${compare(a,b)}`);
or write your custom compare function (that may be challenging):
Compare JavaScript objects
Since your objects are decorated with an id, the easisest way would be to idntify them by that:
let storage = [{"id":"8","name":"Candy Skull", "price":"20000","image":"../images/candyskull- min.JPG","qty":"1"},{"id":"5","name":"Upsidedown House","price":"20000","image":"../images/upsidedownhouse- min.JPG","qty":"1"},{"id":"6","name":"Brooklyn Window","price":"30000","image":"../images/brooklynwindow- min.JPG","qty":"1"},{"id":"4","name":"Hand That Feeds","price":"40000","image":"../images/handthatfeeds- min.JPG","qty":"1"}];
let items = [{"id":"6","name":"Brooklyn Window","price":"30000","image":"../images/brooklynwindow- min.JPG","qty":"1"}, {"id":"5","name":"Upsidedown House","price":"20000","image":"../images/upsidedownhouse- min.JPG","qty":"1"}];
const pluck = (acc, crt) => {
acc.push(crt.id);
return acc;
};
let storageIndexes = storage.reduce(pluck, []);
let itemsIndexes = items.reduce(pluck, []);
let removeIndexes = [];
itemsIndexes.forEach(id => removeIndexes.push(storageIndexes.indexOf(id)));
console.log('storage', storage);
console.log('removed items', items);
removeIndexes.sort().reverse().forEach(index => storage.splice(index,1));
console.log('remaining storage', storage);

Related

unable to remove an object from localStorage using its id

function removeLocalStorageHelper(item, pizzaId) {
console.log(item.productID);
console.log(pizzaId);
if (item.productID == pizzaId) {
console.log("Working fine");
localStorage.removeItem(item);
console.log(localStorage);
}
}
function removeFromLocalStorage(pizzaId) {
var locStore = JSON.parse(localStorage.getItem("selectedProduct"));
var cartRowContents = locStore.map((item) =>
removeLocalStorageHelper(item, pizzaId)
);
}
Whenever the user clicks on "Remove" button, removeFromLocalStorage() function is called which receives a number type variable pizzaId. Now, this pizzaId is stored in the localStorage and I want to remove the object associated with this pizzaId from my localStorage.
CONSOLE OUTPUT
Please help me to remove an object from localStorage using productID.
You can't directly remove an item from the stored array. For that you need to remove the intended element from array and insert again to localStorage like below. Otherwise it'll remove complete list from localStorage.
Note: You need to do the same wile adding new item to the localStorage.
function removeFromLocalStorage(pizzaId) {
var locStore = JSON.parse(localStorage.getItem("selectedProduct"));
var cartRowContents = locStore.filter((item) => item.productID !== pizzaId);
localStorage.setItem("selectedProduct", JSON.stringify(cartRowContents))
}
As I reivew your code, I see that you are retrieving the item from localStorage like this localStorage.getItem("selectedProduct") The question here is do you want to remove the entire locStore or just a part of it. Maybe locStore is a complex collection (array) from multiple products and not a single entity as this code hints
var cartRowContents = locStore.map((item) =>
removeLocalStorageHelper(item, pizzaId));
If this is the case you need to do something like this
var locStore = locStore.filter(function(item, index){
return item.productID != pizzaId;
});
Now you have a locStore collection without the item you want to remove and you have to save it back to localStorage like this
localStorage.setItem('selectedProduct', JSON.stringify(locStore));
Essentially you are overriding the old selectedProduct value in localStorage with a new one that does not contain the removed product.
If for some reason locStore is just a simple JSON object that you want to remove you can do this to delete it from localStorage
localStorage.removeItem('selectedProduct')
Well, if you just need to remove the selectedProduct, you can do it like this:
localStorage.removeItem("selectedProduct");

How to remove a specific item from a Local Storage array?

I'm building a To Do List (codepen) app and I am stuck on removing a specific item from Local Storage. I feel like I have the right code, but It's not working. Here's an overview:
Adding a few tasks sets those tasks in Local Storage. I'm trying to remove that task item when a user clicks the green "checkmark" on that task:
Here's my completeDelete function that moves the task into the "completed" category, and tries to remove that task from Local Storage:
const completeDelete = (e) => {
let checks = results.querySelectorAll(".fa-check"); //Checkmark
let deletes = results.querySelectorAll(".fa-times");
//Complete Task:
checks.forEach((check) => {
if (e.target === check) {
let closestTask = check.closest(".task"); //The task element
let taskText = closestTask.firstElementChild.textContent; //the task text, to be removed
closestTask.remove();
removeTaskFromLS(taskText); //The function that removes the task:
}
Here's the Remove Task function:
const removeTaskFromLS = (task) => {
let storedTasks = JSON.parse(localStorage.getItem("tasks"));
const i = storedTasks.indexOf(task);
if (i > -1) {
let newArr = storedTasks.splice(i, 1);
localStorage.removeItem(JSON.stringify(task)); //not removing from Local Storage
console.log(task);
}
console.log(storedTasks);
};
If I put the following tasks in:
Task 1
Task 2
Task 3
And check complete on Task 1, I'm left with an array of ["Task 2", "Task 3"]. But Local Storage is unaffected, and loads fully with a reload. What am I doing wrong? Thanks in advance.
JSON.parse(localStorage.getItem("tasks")) will give you string instead of Array. Use split post parsing and you will get Array.
const removeTaskFromLS = (task) => {
let storedTasks = JSON.parse(localStorage.getItem("tasks")).split(",");
const i = storedTasks.indexOf(task);
if (i > -1) {
let newArr = storedTasks.splice(i, 1);
localStorage.removeItem(JSON.stringify(task)); //not removing from Local Storage
console.log(task);
}
console.log(storedTasks);
};
It appears like the "tasks" key contains an array of tasks, and you appear to splice it off the parsed array correctly. However, you'll need to persist that spliced array back into localStorage to replace the old one.
Replace the line containing removeItem with:
localStorage.setItem('tasks', JSON.stringify(storedTasks));

Sort array based on intermediate model's attribute

I have three models (I am using Vue/Vuex-ORM on the frontend). Category, CategoryItem and Item.
I'm able to fetch an array of categories, and within each category is an array of items. An intermediate join model defines the relationships of these two models, and I am able to access as such:
// array of categories, each category has array of items
const categories = Category.query().where('pack_id', this.selectedPack.id).with('items').get();
categories.map(category => {
category.items.forEach(item => {
console.log('item.pivot: ', item.pivot); // pivot refers to join model
// how to order items based on item.pivot?
})
})
Within the .forEach, I can access the join model with item.pivot. What I am looking to do however, is sort each category's items based on item.pivot.position.
I started going down a path where the first line inside of the .map I defined a new empty array, and would then theoretically push in a new value based on whether the position was higher or lower, but I couldn't quite wrap my head around how to accomplish this.
Thanks!
Well just my luck. A half hour after posting this question, I figure it out! Here was what I did, in case anyone is curious.
categories() {
const categories = Category.query().where('pack_id', this.selectedPack.id).with('items').get();
categories.forEach(category => category.items.sort(this.compare));
return cats;
}
compare(a, b) {
let comparison = 0;
if (a.pivot.position > b.pivot.position) comparison = 1;
else if (a.pivot.position < b.pivot.position) comparison = -1;
return comparison;
},

On click, loop through each object key

I'm still learning JS and something is harder to understand than others.
Like so:
I am trying to change the theme of google maps by allowing users to click on a custom button.
I was using if else which works great but i wanted to add more themes and using a loop. Each time a user clicks, it selects:
object key 0,
then click again object key 2
and object key 3
and repeat
I can get the object keys and values how I'm lost after that.
This is the theme object
let theme = {
default: null,
night: [multiple objects with nested arrays],
dark: [multiple objects with nested arrays]
}
creating button inside google maps then addEventListener
let themeToggle = document.createElement('button');
themeToggle.classList.add('controlUI');
themeToggle.innerHTML = ('Mode');
themeToggle.title = 'Change map theme';
map.controls[google.maps.ControlPosition.TOP_LEFT].push(themeToggle);
let mode = true;
themeToggle.addEventListener('click', () => {
if (mode) {
map.setOptions({styles: theme.night});
} else {
map.setOptions({styles: theme.default});
}
mode = !mode;
});
Above Works Fine
Im struggling to convert the if else to a loop and select each object key and then adding that to:
map.setOptions({styles: theme.night})
and then on click it loops through each key and repeat
themeToggle.addEventListener('click', () => {
for ( let key in theme) {
map.setOptions({styles: theme[key]});
console.log(theme[key])
}
});
it selects the last one by default and i cant toggle.
Any help would e really appreciated, just trying add all the puzzle together.
Collect the object values into an array, then increment an index with modulo on every click:
const vals = Object.values(theme);
let i = 0;
themeToggle.addEventListener('click', () => {
map.setOptions({styles: vals[i]});
i = (i + 1) % vals.length;
});
While most environments will result in an object's Object.values in ascending numeric followed by insertion order, it's not guaranteed. If you need a guaranteed predictable ordering, use Reflect.ownKeys (or Object.getOwnPropertyNames) instead:
const vals = Reflect.ownKeys(theme)
.map(key => theme[key]);
You can loop through an object like this
var invoice = {
name: 'anik',
age: 29,
designation: 'Full Stack Developer'
}
Object.keys(invoice).map((d,i)=>{
console.log(d +' : '+invoice[d]);
})

Vue computed property overwriting global state without vuex

I have a list of people who have scores. In state I have them listed in an array, one of the items in the array is 'scoreHistory' which is an array of objects containing their scores at different points in time. I want to filter this set for different time periods i.e. -5 days, -30 days so instead of just seeing the overall score I can see the scores if everyone started at 0 say 30 days ago.
I have it (kind of) working. See my code below:
filteredScores () {
if(!this.people) {
return
}
// Here I was trying to ensure there was a copy of the array created in order to not change the original array. I thought that might have been the problem.
let allPeople = this.people.slice(0) // this.people comes from another computed property with a simple getter. Returns an array.
let timeWindow = 30 //days
const windowStart = moment().subtract(timeWindow,'days').toDate()
for (const p of allPeople ) {
let filteredScores = inf.scoreHistory.filter(score => moment(score.date.toDate()).isSameOrAfter(windowStart,'day'))
p.scoreHistory=filteredScores
//calculate new score
p.score = inf.scoreHistory.reduce(function(sum,item) {
return sum + item.voteScore
},0)
}
return allInf
}
I expected it to return to me a new array where each person's score is summed up over the designated time period. It seems to do that OK. The problem is that it is altering the state that this.people reads from which is the overall data set. So once it filters all that data is gone. I don't know how I am altering global state without using vuex??
Any help is greatly appreciated.
Your problem isn't that you're modifying the array, but that you're modifying the objects within the array. You change the scoreHistory and score property of each item in the array. What you want to do instead is create a new array (I recommend using map) where each item is a copy of the existing item plus a new score property.
filteredScores () {
if(!this.people) {
return
}
let timeWindow = 30 //days
const windowStart = moment().subtract(timeWindow,'days').toDate()
return this.people.map(p => {
let filteredScores = p.scoreHistory.filter(score => moment(score.date.toDate()).isSameOrAfter(windowStart,'day'))
//calculate new score
let score = filteredScores.reduce(function(sum, item) {
return sum + item.voteScore
}, 0)
// Create a new object containing all the properties of p and adding score
return {
...p,
score
}
}
})

Categories

Resources