So I'm creating a to-do list, and I'm saving the task to the localStorage, when I refresh the page the task stay but the checkbox gets unchecked how could I stop the checkbox from getting unchecked when I refresh the page
note: I'm creating the checkbox via js so when I add a task it creates the checkbox dynamically.
/* arry that holds tasks */
let tasks = [];
/* create a todo object */
const addTask = (text) => {
const todoTask = {
text,
checked: false,
id: Date.now(),
}
tasks.push(todoTask);
renderTodo(todoTask);
};
const formT = document.querySelector(`[data-new-todo-form]`)
const inputT = document.querySelector(`[data-new-todo-input]`)
const todoList = document.getElementById('todo-list');
formT.addEventListener('submit', e => {
e.preventDefault()
let text = inputT.value.trim();
if(text !== '') {
addTask(text);
inputT.value = '';
inputT.focus();
}
})
const renderTodo = (todoTask)=> {
localStorage.setItem('tasksRef', JSON.stringify(tasks));
const item = document.querySelector(`[data-key='${todoTask.id}']`);
if (todoTask.deleted) {
// remove the item from the DOM
item.remove();
return
}
const isChecked = todoTask.checked ? 'done': '';
const node = document.createElement('li')
node.setAttribute('class', `todo-item ${isChecked}`);
node.setAttribute('data-key', todoTask.id);
node.innerHTML = `
<input class="js-tick save-cb-state" id="${todoTask.id}" type="checkbox"/>
<span>${todoTask.text}</span>
<img class="delete" width="15px" height='15px' src="/images/icon-cross.svg" alt="cross">`
;
todoList.append(node);
if (item) {
node.replaceWith(item)
} else {
todoList.append(node)
}
}
todoList.addEventListener('click', e => {
if (e.target.classList.contains('js-tick')) {
const itemKey = e.target.parentElement.dataset.key;
toggleDone(itemKey);
}
if (e.target.classList.contains('delete')) {
const itemKey = e.target.parentElement.dataset.key;
deleteTodo(itemKey);
}
});
const toggleDone = (key) => {
const index = tasks.findIndex(task=> task.id === Number(key));
tasks[index].checked = !tasks[index].checked;
renderTodo(tasks[index]);
}
const deleteTodo = (key) => {
const index = tasks.findIndex(item => item.id === Number(key));
const todoTask = {
deleted: true,
...tasks[index]
};
tasks = tasks.filter(item => item.id !== Number(key));
renderTodo(todoTask);
}
document.addEventListener('DOMContentLoaded', () => {
const ref = localStorage.getItem('tasksRef');
if (ref) {
tasks = JSON.parse(ref);
tasks.forEach(task => {
renderTodo(task);
});
}
});
when you add checkbox to DOM you should set Checked attribute for that.
change renderTodo with this code:
node.innerHTML = `
<input class="js-tick save-cb-state" id="${todoTask.id}" type="checkbox" ${isChecked ? "checked" : ""}/>
<span>${todoTask.text}</span>
<img class="delete" width="15px" height='15px' src="/images/icon-cross.svg" alt="cross">`
;
if you putted your html code I could create live Demo, I think this change will be fixed your problem
Related
I'm trying to get list from the onClick function but I can't if there any solution, please.
here is my full code link
let abe = []
const click = (e) => {
const cityy = e.target.value
const checkUsername = obj => obj.city === cityy;
const result = abe.some(checkUsername)
if (!result) {
abe.push({ "city": cityy})
}
if (!e.target.checked) {
const indexe = abe.findIndex(p => p.city === cityy)
abe.splice(indexe, 1)
}
const simo = watch("simo")
let list = abe.map((list) => list.city).join(" , ")
}
Is the click function triggered in the first place? You have actually missed to show where the click function is used.
Here is an example. Looks like you have to store cities in the state.
const [citiesList, setCitiesList] = useState<string[]>([]);
const click = (e) => {
const cityy = e.target.value
const checkUsername = obj => obj.city === cityy;
const result = citiesList.some(checkUsername)
if (!result) {
setCitiesList(prevState => [...prevState, cityy]);
}
if (!e.target.checked) {
const cList = [...citiesList];
const indexe = cList.findIndex(p => p === cityy)
cList.splice(indexe, 1);
setCitiesList(cList);
}
const simo = watch("simo");
}
the idea of this stuff is that user can add parameters to the SQL editor from two inputs, one for the parameter itself and the other one for its value
and if user writes in SQL editor, automatically adds inputs that are the parameter and the value.
From SQL editor to inputs it works fine, cause I'm sending a regex. The trouble is located where I try to send the inputs to SQL Editor. I think is more like a logic problem, but I still don't find the solution.
The point is that by adding one parameter from the input, it adds more than required, even though I always clean it before adding, but apparently that doesn't affect.
This is the code
import React, { useEffect, useState} from 'react';
import SQLContainerInput from '../Components/SQLContainerInput';
........
function arrayParamsExec(stringSql) {
const paramsQueryText = [...stringSql.matchAll(/{{(\w+)}}/ig)];
const newArray = paramsQueryText.map(item => item[1]);
return newArray;
}
const initalStateCurrentChartInfo = {
SQLQuery: '',
dataType: 'TABLE',
columns: [],
};
const CustomSQLEditor = ({
fromQuery, // del Redux
}) = {
const [currentChartInfo, setCurrentChartInfo] = useState(
initalStateCurrentChartInfo,
);
const [params, setParams] = useState([]);
const [textSql, setTextSql] = useState('');
useEffect(() => {
....
let sqlDefaultString = '';
sqlDefaultString = fromQuery.internal_name
? `SELECT * FROM \`${fromQuery.internal_name}__${fromQuery.items[0]}\` LIMIT 20`
: '';
setCurrentChartInfo({
...currentChartInfo,
SQLQuery: `${sqlQuery}`,
});
},[fromQuery]);
// ------------------params---------------------
const addProperty = () => {
setParams([
...params,
{ name: '', value: '' },
]);
};
const updateProperty = (event, index, key) => {
const newProperties = [...params];
newProperties[index][key] = event?.target?.value;
// agregar parĂ¡metros al editor SQL
let sqlParams = textSql;
if (key === 'name') {
params.forEach(p => {
if (p.name && /^\w+$/i.test(p.name)) {
sqlParams += `{{${p.name}}}`;
}
});
setTextSql('');
setTextSql(`${sqlParams}`);
}
setParams(newProperties);
};
const deleteProperty = index => {
const newProperties = [...params];
newProperties.splice(index, 1);
const newTextSQL = replaceAll(textSql, `{{${params[index]?.name}}}`, '');
setTextSql(newTextSQL);
setParams(newProperties);
};
// ------------------end params---------------------
const changeTextEditor = (valueEditor) => {
const namesParams = arrayParamsExec(valueEditor);
const newProperties = namesParams.map((pName) => {
const valueNew = params.find(p => p.name === pName);
return {name: pName, value: valueNew?.value || ''};
});
setParams(newProperties);
setTextSql(valueEditor);
}
return (
<>
<SQLContainerInput
button={{
onClick: handleSubmit,
}}
input={{
value: `${textSql}\n`,
onChange: changeTextEditor,
}}
/>
<DymanicKeyValueInputInput
properties={params}
updateProperty={updateProperty}
deleteProperty={deleteProperty}
addProperty={addProperty}
/>
</>
);
}
Then, I tried as a solution set another value which is textSql, that takes care of placing the concatenated string, and the string coming from redux is fromQuery. The redux string is set in the sqlParams variable, when is added concatenates with the params and then, I clean textSql
......
const updateProperty = (event, index, key) => {
const newProperties = [...params];
newProperties[index][key] = event?.target?.value;
// agregar parĂ¡metros al editor SQL
let sqlParams = currentChartInfo.SQLQuery;
if (key === 'name') {
params.forEach(p => {
if (p.name && /^\w+$/i.test(p.name)) {
sqlParams += `{{${p.name}}}`;
}
});
setTextSql('');
setTextSql(`${sqlParams}`);
}
setParams(newProperties);
};
......
The trouble in there is that if I directly write from SQL editor, it resets the whole string, I mean, everything that has been written but there it works when I put the params and it's not repeated. I don't find a way to do that, so I'm sorry for the ignorance if I'm doing something wrong.
For example, when I write a large SQL text.
When a parameter is added from the input, it resets.
Video with the error demo: https://www.youtube.com/watch?v=rQBPOPyeXlI
Repo's url: https://gitlab.com/albert925/parametrosui-a-editor-sql
try this:
import { useState, useEffect } from 'react';
import DymanicKeyValueInputInput from './components/DymanicKeyValueInputInput';
import SQLContainerInput from './components/SQLContainerInput';
import { fromQuery } from './bd/data';
import { replaceAll, arrayParamsExec } from './utils/strings';
import { Content, ContentAddButton } from './stylesApp';
const initalStateCurrentChartInfo = {
SQLQuery: '',
columns: [],
};
function App() {
const [currentChartInfo, setCurrentChartInfo] = useState(
initalStateCurrentChartInfo,
);
const [params, setParams] = useState([]);
const [textSql, setTextSql] = useState('');
const [endSql, setendSql] = useState('');
useEffect(() => {
let sqlQuery = '';
sqlQuery = fromQuery.internal_name
? `SELECT * FROM \`${fromQuery.internal_name}__${fromQuery.items[0]}\` LIMIT 20`
: '';
setCurrentChartInfo({
...currentChartInfo,
SQLQuery: `${sqlQuery}`,
});
setTextSql(sqlQuery);
},[fromQuery]);
useEffect(()=>{
const endSql = replaceAll(textSql, '\n', '');
setendSql('')
setendSql(endSql);
},[textSql]);
const cleanSQLString = (string) => {
//corto la cadena en {{
//reemplazo {{ por "" en cada item
//devuelvo el array con los reemplazos
return string.split("{{").map(item=>item.replace("}}",""));
}
// ------------------params---------------------
const addProperty = () => {
setParams([
...params,
{ name: '', value: '' },
]);
};
const updateProperty = (event, index, key) => {
const newProperties = [...params];
newProperties[index][key] = event?.target?.value;
let currentSQL = cleanSQLString(endSql);
// clean the string so that then add parameters to sql
if (key === 'name' && /^\w+$/i.test(event?.target?.value)) {
let sqlParams;
if(currentSQL.length > 1){
sqlParams = `${currentSQL[0]}`;
}else{
sqlParams = `${endSql}`;
}
params.forEach(p => {
if (p.name && /^\w+$/i.test(p.name)) {
sqlParams += `{{${p.name}}}`;
}
});
setTextSql('');
setTextSql(`${sqlParams}`);
/*if(currentSQL.length > 1){
sqlParams = `${currentSQL[0]}`;
}else{
sqlParams = `${endSql}`;
}
console.log(sqlParams)
// get parameter positions by regular expression
const searchParamRegedix = arrayParamsExec(endSql);
console.log(searchParamRegedix, 'searchParamRegedix')
if (searchParamRegedix.paramsQueryText.length > 0) {
console.log(111)
// get the position
const searchPosition = searchParamRegedix.paramsQueryText[index]?.index;
// remove the old word in braces
const deleteOldParam = replaceAll(sqlParams, `${searchParamRegedix.list[index]}`, '');
// the remaining string is removed from the obtained position
const deleteFirtsLetter = deleteOldParam.substr(searchPosition + 2);
// the string is removed from the beginning to the position obtained
const deleteRemaining = deleteOldParam.substr(0, searchPosition + 2)
// the string of the beginning and end is combined with its parameter
const contantString = deleteRemaining + event?.target?.value + deleteFirtsLetter;
// the entire string is overwritten in the state
setTextSql(contantString);
}
else{
params.forEach(p => {
if (p.name && /^\w+$/i.test(p.name)) {
sqlParams += `{{${p.name}}}`;
}
});
setTextSql('');
setTextSql(`${sqlParams}`);
}*/
}
setParams(newProperties);
};
const deleteProperty = index => {
const newProperties = [...params];
newProperties.splice(index, 1);
const newTextSQL = replaceAll(textSql, `{{${params[index]?.name}}}`, '');
setTextSql(newTextSQL);
setParams(newProperties);
};
// ------------------end params---------------------
const changeTextEditor = (valueEditor) => {
const namesParams = arrayParamsExec(valueEditor);
// keep the value with the parameter with respect to adding a new parameter in the sql editor,
// but if the parameter word is changed, the value is deleted
const newProperties = namesParams.list.map((pName) => {
const valueNew = params.find(p => p.name === pName);
return {name: pName, value: valueNew?.value || ''};
});
setParams(newProperties);
setTextSql(valueEditor);
}
return (
<Content>
<div>
<SQLContainerInput
input={{
value: `${endSql}\n`,
onChange: changeTextEditor,
}}
/>
</div>
<div>
<h2>PARAMS</h2>
<ContentAddButton>
<DymanicKeyValueInputInput
properties={params}
updateProperty={updateProperty}
deleteProperty={deleteProperty}
addProperty={addProperty}
id="sqlapiparams"
className="isMultipleInpustSelects"
tilesColumns={["", ""]}
inputsPlaceholder={["My_Parameter", "Type params value"]}
isIconTransfer
isIconError
/>
</ContentAddButton>
</div>
</Content>
);
}
export default App;
I am using React. On click of a button, the following function is executed:
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const updatedData = [...prevData];
const updatedItem = updatedData.filter((ele) => ele.id === idValue)[0];
updatedItem.completed = true;
const newData = updatedData.filter((ele) => ele !== updatedItem);
newData.unshift(updatedItem);
return newData;
});
};
My data is an array of objects like this:
[{userId: 1, id: 2, title: "task 1", completed: true}, .....].
Basically I want to move the updated item to the start of the array. Is there any better solution for this?
updatedItem should not be mutated. And this string const newData = updatedData.filter((ele) => ele !== updatedItem); is not fine. You can do it like this :
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const targetItem = prevData.find((ele) => ele.id === idValue);
const updatedItem = { ...targetItem, completed: true };
const filteredData = prevData.filter((ele) => ele.id !== idValue);
return [updatedItem, ...filteredData];
});
};
Even better to reducing an extra filter:
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const targetIndex = prevData.findIndex((ele) => ele.id === idValue);
return [{ ...prevData[targetIndex], completed: true }].concat(prevData.slice(0, targetIndex + 1)) .concat(
prevData.slice(targetIndex + 1)
)
});
};
First find index of updated element using Array.findIndex(), then remove the same element using Array.splice() and add it to front of the array.
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const updatedData = [...prevData];
const index = updatedData.findIndex(obj => obj.id === idValue);
const [updatedItem] = updatedData.splice(index, 1);
updatedItem.completed = true;
updatedData.unshift(updatedItem);
return updatedData;
});
};
The simplest one with only one forEach.
const completeTaskHandler = idValue => {
setData(prevData => {
let updatedItem = {}, newData = [];
prevData.forEach((ele) => {
if (ele.id === idValue) {
updatedItem = ele;
updatedItem.completed = true;
} else {
newData.push(ele);
}
});
newData.unshift(updatedItem);
return newData;
});
};
I'm trying to delete item's from a todo list that I saved in the localStorage.
I was thinking about doing something similar to this:
let deleteTodos = () => {localStorage.removeItem(todos["hola"]); };
In this part of the code :
list.addEventListener("click", (e) => {
if (e.target.classList.contains("delete")) {
e.target.parentElement.remove();
localStorage.setItem("todos", JSON.stringify(todos));
}
});
And here is the whole code to give you and idea of the whole app
const addForm = document.querySelector(".add");
const list = document.querySelector(".todos");
const search = document.querySelector(".search input");
var todos = [];
var todosString = localStorage.getItem("todos");
var todos = JSON.parse(todosString);
// generate new toDo's
const generateTemplate = (todos) => {
let html = ` <li class="list-group-item d-flex justify-content-between align-items-center">
<span>${todos}</span><i class="far fa-trash-alt delete"></i>
</li>`;
list.innerHTML += html;
};
todos.forEach((todo) => generateTemplate(todo));
// submit the todo
addForm.addEventListener("submit", (e) => {
e.preventDefault();
let todo = addForm.add.value.trim();
if (todo.length) {
todos.push(todo);
localStorage.setItem("todos", JSON.stringify(todos));
generateTemplate(todo);
addForm.reset();
}
});
// delete todo's
list.addEventListener("click", (e) => {
if (e.target.classList.contains("delete")) {
e.target.parentElement.remove();
localStorage.setItem("todos", JSON.stringify(todos));
}
});
// filter the toDo's
const filterTodos = (term) => {
Array.from(list.children)
.filter((todo) => !todo.textContent.toLowerCase().includes(term))
.forEach((todo) => todo.classList.add("filtered"));
Array.from(list.children)
.filter((todo) => todo.textContent.toLowerCase().includes(term))
.forEach((todo) => todo.classList.remove("filtered"));
};
search.addEventListener("keyup", () => {
const term = search.value.trim().toLowerCase();
filterTodos(term);
});
If you're saving the stringified todos in localStorage under "todos", you cannot then delete a single item via localStorage.removeItem(todos["hola"]).
Without commenting on whether you should be using localStorage in this way, to delete an item you'll need to replace the entire object in localStorage:
const deleteTodo = (label) => {
// get the items from storage
const oldTodos = JSON.parse(localStorage.getItem('todos'));
// delete the specified item. (however you need to do it. this might not work with your specific data structure)
const newTodos = oldTodos.filter(({ label: x }) => x === label);
// re-reserialize to local storage
localStorage.setItem('todos', JSON.stringify(newTodos));
};
When I add an item after refresh the other items are lost and only the item that was added after the refresh of the page remains
My code:
let buttonsDom = document.querySelectorAll(elementsStr.itemsBtn)
const buttons = [... buttonsDom]
window.addEventListener('DOMContentLoaded', () => {
Storage.saveProducts(itemStr)
let cart = []
buttons.forEach((btn) => {
const id = btn.dataset.id;
if(localStorage.getItem('cart')) {
if(Storage.findCart(id)){
btn.innerText = 'in cart';
btn.disabled = true;
}
btn.addEventListener('click', () => {
btn.innerText = 'in cart';
btn.disabled = true;
const cartItem = { ...Storage.findProduct(id), amount:1 }
cart = [...cart, cartItem]
Storage.saveCart(cart)
})
})
})
export class Storage {
static saveProducts(products) {
localStorage.setItem("items", products);
}
static findProduct(id) {
const products = JSON.parse(localStorage.getItem("items"));
return products.find((item) => item._id === id);
}
static findCart(id) {
const product = JSON.parse(localStorage.getItem("cart"));
return product.find((item) => item._id === id);
}
static saveCart(cart) {
localStorage.setItem("cart", JSON.stringify(cart));
}
}
Maybe should you fill the cart in retrieving it from the localStorage no ?
let buttonsDom = document.querySelectorAll(elementsStr.itemsBtn)
const buttons = [... buttonsDom]
window.addEventListener('DOMContentLoaded', () => {
Storage.saveProducts(itemStr)
let cart = Storage.retrieveCart(); // <---- Here
buttons.forEach((btn) => {
const id = btn.dataset.id;
if(localStorage.getItem('cart')) {
if(Storage.findCart(id)){
btn.innerText = 'in cart';
btn.disabled = true;
}
btn.addEventListener('click', () => {
btn.innerText = 'in cart';
btn.disabled = true;
const cartItem = { ...Storage.findProduct(id), amount:1 }
cart = [...cart, cartItem]
Storage.saveCart(cart)
})
})
})
export class Storage {
static saveProducts(products) {
localStorage.setItem("items", products);
}
static findProduct(id) {
const products = JSON.parse(localStorage.getItem("items"));
return products.find((item) => item._id === id);
}
static findCart(id) {
const product = JSON.parse(localStorage.getItem("cart"));
return product.find((item) => item._id === id);
}
static saveCart(cart) {
localStorage.setItem("cart", JSON.stringify(cart));
}
static retrieveCart() { // <---- Here
let cart = localStorage.getItem("cart");
if (cart === null) {
return [];
}
try {
return JSON.parse(cart);
} catch(e) {
return [];
}
}
}