How to keep array of objects in localstorage - javascript

i'm trying to use localstorage for my array of objects but it doesn't work (without the localstorage it works perfectly). after a few days of tries i'm trying here. can anyone help please?
The Code:
<script>
var notes = JSON.parse(localstorage.getitem("notes") || "[]");
function todo() { // Gets invoked by a submit button
var list = document.getElementById("tasklist").value;
var taskdate = document.getElementById("taskdate").value;
var tasktime = document.getElementById("tasktime").value;
const note = {
list: list,
taskdate: taskdate,
tasktime: tasktime
}
notes.push(note);
for (i = 0; i < notes.length; i++) {
document.getElementById("name").append(notes[i].list);
}
localStorage.setItem("notes", JSON.stringify(notes));
}
</script>

You have used incorrect keyword.
localstorage => localStorage
getitem => getItem

Your code seems valid except for some syntax errors. Use a good IDE or code editor or whatever to show you that errors. I recommend vscode
var notes = JSON.parse(localStorage.getItem("notes") || "[]");
function todo() { // Gets invoked by a submit button
var list = document.getElementById("tasklist").value;
var taskdate = document.getElementById("taskdate").value;
var tasktime = document.getElementById("tasktime").value;
const note = {
list: list,
taskdate: taskdate,
tasktime: tasktime
};
notes.push(note);
for (let i = 0; i < notes.length; i++) {
document.getElementById("name").append(notes[i].list);
}
localStorage.setItem("notes", JSON.stringify(notes));
}

try please declare as array, note variable
var note = array();
note = {
list: list,
taskdate: taskdate,
tasktime: tasktime
}

Related

my html created html in my to do list keeps disapering when i reload the page

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.

Why can we omit a return statement in this usage of the method map()

I am currently going over the WebRTC tutorial on their docs when I noticed that they use forEach after their usage of map(). In order to use forEach and expect a value instead of undefined array, map() would have needed to return an array, which I don't see how it can, because it doesn't return anything.
function updateCameraList(cameras) {
const listElement = document.querySelector('select#availableCameras');
listElement.innerHTML = '';
cameras.map(camera => {
const cameraOption = document.createElement('option');
cameraOption.label = camera.label;
cameraOption.value = camera.deviceId;
}).forEach(cameraOption => listElement.add(cameraOption));
}
The code will not work since the map returns nothing.
Here is an alternative method
function updateCameraList(cameras) {
document.getElementById('availableCameras').innerHTML = cameras
.map(({label, deviceId}) => `<option value="${deviceId}">${label}</option>`)
.join("");
}
I learned today that we now can use label instead of text
https://jsfiddle.net/mplungjan/osyLzqk2/
Here is a safer version since there is a tiny possibility for XSS
function updateCameraList(cameras) {
const sel = document.getElementById('availableCameras')
cameras.forEach(({label, deviceId}) => {
const option = new Option(label,deviceId);
sel.add(option)
})
}
And here is a non working attempt of XSS - at least it does nothing in Chrome
const cameras = [{ deviceId : `xss"></option></select><img src="x" onerror="alert(1)" />` , label:"bla" }]
function updateCameraList(cameras) {
const xssString = cameras
.map(({label, deviceId}) => `<option value="${deviceId}">${label}</option>`)
.join("")
console.log(xssString)
document.getElementById('availableCameras').innerHTML = xssString;
}
updateCameraList(cameras)
<select id="availableCameras"></select>
The code is clearly missing a line. The code should be pushing undefined into an array and that would be appended to the select.
function updateCameraList(cameras) {
const listElement = document.querySelector('select#availableCameras');
listElement.innerHTML = '';
cameras.map(camera => {
const cameraOption = document.createElement('option');
cameraOption.label = camera.label;
cameraOption.value = camera.deviceId;
return cameraOption;
}).forEach(cameraOption => listElement.add(cameraOption));
}
Now why would we need to loop twice, it is a bit of a waste of time. So I would just loop once.
function updateCameraList(cameras) {
const listElement = document.querySelector('select#availableCameras');
listElement.innerHTML = '';
cameras.forEach(camera => {
const cameraOption = document.createElement('option');
cameraOption.label = camera.label;
cameraOption.value = camera.deviceId;
listElement.add(cameraOption));
});
}

bsmultiselect: how to get list or array of items picked by the user?

I stumbled upon the bsmultiselect library for a project. The interface looks good but seems like the documentation needs some more work, hence i cannot figure out how to return the list of items selected by the user. I'd appreciate it if someone could give me a hint at least.
here's my code for now:
const instSel = document.getElementById('instrument-select');
let x = new xhr();
x.get("GET", "instruments.json", (rsp) => {
instr = JSON.parse(rsp)
for (i in instr) {
let optGr = document.createElement("optgroup");
optGr.setAttribute("label", instr[i]["name"]);
for (x in instr[i]["instruments"]) {
let opt = document.createElement("option");
opt.setAttribute('value', instr[i]["instruments"][x]);
opt.setAttribute('class', 'dropdown-item');
opt.textContent = instr[i]["instruments"][x];
optGr.appendChild(opt);
}
instSel.appendChild(optGr);
console.log(optGr);
}
bs = dashboardcode.BsMultiSelect("#instrument-select");
console.log(bs);
});
document.body.addEventListener('dblclick', () => {
console.log(bs.getPicks()); // this returns the html only
});
Thanks in advance for any help!
Update
Managed to collect the data by using the e.target event.
instSel.onchange = (e) => {
// loop through all the items
for (let i = 0; i < e.target.length; i++;) {
if (e.target[i].selected) { // if the item is selected
console.log(e.target[i].textContent)
}
}
}

Array remains empty after push

I declared an array, But when I push elements inside it, it remains Empty. Here's my Code :
var catsObjectId = new Array();
var data = new Array();
Recipe.find((err,doc3)=> {
data = doc3;
for (var i = 0; i < data.length; i++) {
catsObjectId.push([]);
data[i]['categories'].forEach((item, index) => {
Recipecat.findOne({_id: item}, (err,result)=> {
item = result.name;
catsObjectId.push(item);
});
})
}
console.log(catsObjectId);
});
Here's the Recipe schema :
var recipeSchema = Schema({
categories: [{
type: Schema.Types.ObjectId,
ref: 'RecipeCat',
}]
});
and Here's the Recipecat schema :
var recipecatSchema = new Schema({
name: {
type: String,
required: true
}
});
I want to replace objectIds for recipeCats with their names.
When I log 'catsObjectId', It shows an empty array.
What Seems to be the Problem?
Thanks In advance!
(I understand this question is a bit old, but if you still need help)
That's because you're pushing to an array which is outside the callback and the async nature of JavaScript kicking in.
Here's simple explanation why it's empty
var catsObjectId = new Array();
var data = new Array();
Recipe.find((err,doc3)=> {
// say execution 1
for (var i = 0; i < data.length; i++) {
catsObjectId.push([]);
data[i]['categories'].forEach((item, index) => {
// say execution 2
Recipecat.findOne({_id: item}, (err,result)=> {
item = result.name;
catsObjectId.push(item);
});
})
}
// say execution 3
console.log(catsObjectId);
});
First execution 1 is executed. Within this forEach iterates over each item and fires execution 2. Then continues to execute execution 3.
The problem is execution 2 is asynchronous and the value is returned sometime in the future. This future is after excution 3 is executed. When Recipecat.findOne finishes execution, the callback within then(result.. is called. But console.log(catsObjectId) is already executed and catsObjectId was empty at the time of execution.
You should either use catsObjectId within the callback .then((data) => // use data here) or use the async/await to make it sync like.
Note await is only valid inside async function
async function getSomeNames() {
try {
const data = await Recipe.find();
// docs is an array of promises
const docs = data.map((item, index) => {
Recipecat.findOne({_id: item})
});
// items is an array of documents returned by findOne
const items = await Promise.all(docs);
// now you can map and get the names
const names = items.map(item => item.name);
} catch (e) {
// handle error
console.error(e);
}
}
getSomeNames()
Your pushing an empty array every time it goes through the for loop. Try deleting this line.
catsObjectId.push([]);
You have to use promises in order to control your code. Try the following code and tell me if an error exists.
Recipe.find().then(doc3 => {
data = doc3;
for (var i = 0; i < data.length; i++) {
data[i]['categories'].forEach((item, index) => {
Recipecat.findOne({_id: item}).then(result => {
item = result.name;
catsObjectId.push(item);
});
})
}
console.log(catsObjectId);
})
.catch(err => {
console.log(err);
});
Recently ran into a similar problem. Fix for me was to replace the forEach loop with a simple for loop. It turned out, that the forEach loop is not bothering about async-await, but the for loop is.
Here is my code snippet:
let orders = await order_db.find({ profileID: req.body.id }).exec();
let final_orders = [];
for(let i=0; i<orders.length; i++){
let order = orders[i];
if (order.shopID != null) {
let shop = await shop_db.find({ _id: order.shopID }).exec();
let shopName = shop[0].name;
let shopEmail = shop[0].email;
let shopAddress = shop[0].address;
let shopPhone = shop[0].phone;
let updated_order = { ...order._doc, shopName, shopEmail, shopAddress, shopPhone };
final_orders.push(updated_order);
}
else {
let shopName = "";
let shopEmail = "";
let shopPhone = "";
let shopAddress = "";
let updated_order = { ...order._doc, shopName, shopEmail, shopAddress, shopPhone };
final_orders.push(updated_order);
}
};

How to preserve applied filters on browser back button in react js

In my application, there are filters on all list pages.
Now as per requirement, I want to preserve applied filters on browser back button.
E.g say user has applied filters on sales list page.
After that he clicks on one record to go to sales edit page.
Now if user hit browser back button then those applied filters should remain.
To accomplish this i did like below.
On sales list page ,
componentWillMount() {
const { list, checkReportClose,
updateReportCloseStatus } = this.props;
const { inPopState } = this.state;
window.onpopstate = () => {
if (!_.isEmpty(list)) {
this.setState({ filters: JSON.parse(list.input.filters),
inPopState: true}, this.loadList);
}
}
if(!inPopState && inPopState != undefined &&
checkReportClose == false) {
this.loadList();
}
}
Using above code it works fine but while back button it calls the lis page api (this.loadList) twise.
Please suggest some new solution or modification in existing one.
Thanks.
I would suggest using the url to store filters as parameters.
I've written an example below. Let's use http://skylerfenn.com/news?order=asc&category=feature&year=2017 as the url for the functions below.
Step 1: Call getParameters() in the window.onpopstate and store the parameters in state. Using the URL above, getParameters() will return the parameters as the object { order: 'asc', category: 'feature', year: '2017' }.
function getParameters() {
let parameters = window.location.search.replace('?', '');
let currentParameters = {};
if (Object.keys(parameters).length) {
parameters = parameters.split('&');
for (let i = 0; i < parameters.length; i++) {
let parameter = parameters[i].split('=');
currentParameters[parameter[0]] = parameter[1];
}
}
return currentParameters;
}
Step 2: Pass any new parameters to the getNewParameters function below as an object. For example, calling getNewParameters({ order: 'desc', year: null }) returns the object { order: 'desc', category: 'feature' }. Notice that it removes year since it's null.
function getNewParameters(newParameters) {
const parameters = getParameters();
const keys = Object.keys(newParameters);
for (let i = 0; i < keys.length; i++) {
const value = newParameters[keys[i]];
parameters[keys[i]] = value;
if (!value) {
delete parameters[keys[i]];
}
}
return parameters;
}
Step 3: Use the result from getNewParameters to update state. Also pass the result to function below to update your url.
updateUrl(parameters) {
let search = '';
let j = 0;
let separator = '?';
Object.keys(parameters).forEach((key) => {
let value = parameters[key];
if (value) {
if (j !== 0) {
separator = '&';
}
search += `${separator}${key}=${value}`;
j++;
}
});
let newUrl = `${location.origin}${location.pathname}${search}`;
// prevents pushing same url if function won't change url.
if (location.href !== newUrl) {
history.pushState(null, null, newUrl);
}
}

Categories