I have my search box almost finished, I think the logic of the code is correct, but it is throwing me an error on the line that I want to add the "hide" class, if someone would be so kind as to give me a hand I would appreciate it.
Error:
Uncaught TypeError: Cannot read properties of undefined (reading 'classList') at prueba.js:301:19 at Array.forEach (<anonymous>) at HTMLInputElement.<anonymous> (prueba.js:296:13)
Code:
const traigoJson = async()=> {
const respuesta = await fetch ('../stock.json')
const data = await respuesta.json()
let contenedor = document.getElementById("container")
data.forEach((producto, indice) => {
let card = document.createElement("div")
card.classList.add("col-xl-3", "col-lg-3", "col-md-6", "col-sm-6", "hide", `${producto.deporte}`, "products")
card.innerHTML = `<div class="glasses_box">
<figure><img src="${producto.imagen}" alt="esta es una foto de ${producto.nombre}"/></figure>
<h3><span class="blu">$</span>${producto.precio}</h3>
<p class="product-name">${producto.nombre}</p>
<button type="button" class="btn btn-outline-secondary boton-comprar" id="asd" onClick = "agregarAlCarrito (${indice})">COMPRAR</button>
</div>`
contenedor.appendChild(card)
producto.card = card;
})
}
traigoJson()
const searchInput = document.getElementById("search-input")
const card = document.querySelectorAll(".products")
searchInput.addEventListener("input", e => {
const value = e.target.value.toLowerCase()
productos.forEach(producto => {
const isVisible =
producto.nombre.toLowerCase().includes(value) ||
producto.deporte.toLowerCase().includes(value)
producto.card.classList.toggle("hide", !isVisible)
})
})
You create a card element for each product, but then do not add the card to the product item. And so the code errors during the search when trying to get the value of producto.card.
In the code below I've added this line to the forEach loop:
producto.card = card;
This change should be enough to get you started.
let productos = [{
nombre: "1",
deporte: "d",
precio: "100",
imagen: "https://via.placeholder.com/100"
},
{
nombre: "2",
deporte: "d",
precio: "100",
imagen: "https://via.placeholder.com/100"
},
{
nombre: "3",
deporte: "d",
precio: "100",
imagen: "https://via.placeholder.com/100"
},
{
nombre: "4",
deporte: "d",
precio: "100",
imagen: "https://via.placeholder.com/100"
},
{
nombre: "5",
deporte: "d",
precio: "100",
imagen: "https://via.placeholder.com/100"
}
];
const crearCards = () => {
let contenedor = document.getElementById("container")
productos.forEach((producto, indice) => {
let card = document.createElement("div")
card.classList.add("col-xl-3", "col-lg-3", "col-md-6", "col-sm-6", "hide", `${producto.deporte}`, "products")
card.innerHTML = `<div class="glasses_box">
<figure><img src="${producto.imagen}" alt="esta es una foto de ${producto.nombre}"/></figure>
<h3><span class="blu">$</span>${producto.precio}</h3>
<p class="product-name">${producto.nombre}</p>
<button type="button" class="btn btn-outline-secondary boton-comprar" id="asd" onClick = "agregarAlCarrito (${indice})">COMPRAR</button>
</div>`
contenedor.appendChild(card)
producto.card = card;
})
}
const searchInput = document.getElementById("search-input")
const card = document.querySelectorAll(".products")
searchInput.addEventListener("input", e => {
const value = e.target.value.toLowerCase()
productos.forEach(producto => {
const isVisible =
producto.nombre.toLowerCase().includes(value) ||
producto.deporte.toLowerCase().includes(value)
producto.card.classList.toggle("hide", !isVisible)
})
})
crearCards();
.hide {
display: none;
}
<div id="container" class="container">
Type 1, 2, 3, 4, or 5 into the search box to test:
<input id="search-input" class="form-control my-2" placeholder="search">
<div class="products"></div>
</div>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.1/dist/js/bootstrap.bundle.min.js"></script>
Related
so i am trying to make a filter for my products. And for that i initialised a state in a component called Product that has all the filters. And then, based on the input of the checkboxes that are in a child component that is called Category, the filter state changes.
here is my code :
Product.jsx:
import { useState } from "react";
import Filter from "./Filter.jsx";
import styles from "../styles/products.module.css";
const products = {
prod1: {
id: 1,
name: "prod1",
category: "laptops",
price: "80",
description: { brand: "lenovo", processor: "intel core i7" },
},
prod2: {
id: 2,
name: "prod2",
category: "laptops",
price: "10",
description: { brand: "lenovo", processor: "intel core i7" },
},
prod3: {
id: 3,
name: "prod3",
category: "laptops",
price: "100",
description: { brand: "msi", processor: "intel core i5" },
},
prod4: {
id: 4,
name: "prod4",
category: "laptops",
price: "200",
description: { brand: "msi", processor: "intel core i3" },
},
prod5: {
id: 5,
name: "prod5",
category: "phones",
price: "50",
description: { brand: "samsung", ram: "8gb", storage: "64gb" },
},
prod6: {
id: 6,
name: "prod6",
category: "phones",
price: "50",
description: { brand: "infinix", ram: "4gb", storage: "128gb" },
},
prod7: {
id: 7,
name: "prod7",
category: "phones",
price: "50",
description: { brand: "oppo", ram: "8gb", storage: "256gb" },
},
prod8: {
id: 8,
name: "prod8",
category: "accessories",
price: "99",
description: { type: "keyboard" },
},
prod9: {
id: 9,
name: "prod9",
category: "accessories",
price: "75",
description: { type: "mouse" },
},
};
function Products() {
const filter = {
laptops: {
brand: [],
processor: [],
},
phones: {
brand: [],
ram: [],
storage: [],
},
accessories: {
type: [],
},
};
const [filterList, setFilterList] = useState(filter);
const [category, setCategory] = useState("all");
const [range, setRange] = useState(9999);
const getFilterState = () => {
return filterList;
};
const setFilterState = (list) => {
setFilterList(list);
};
return (
<div className={styles.productsContainer}>
<Filter
range={[range, setRange]}
category={[category, setCategory]}
getfilterList={getFilterState}
setFilterState={setFilterState}
/>
<table className={styles.productsListContainer}>
<tr className={styles.tableColumn}>
<th className={styles.tableRow}>id</th>
<th className={styles.tableRow}>Name</th>
<th className={styles.tableRow}>price</th>
</tr>
{Object.keys(products).map((key) => {
let flag = true;
const product = products[key];
const categ = product.category;
if (
(categ === category || category === "all") &&
product.price <= range
) {
Object.entries(filter[categ]).forEach(([key, value]) => {
if (
!(
value.length === 0 || value.includes(product.description[key])
)
) {
flag = false;
}
});
if (flag) {
return (
<tr className={styles.tableColumn} key={product.id}>
<td className={styles.tableRow}>{product.id}</td>
<td className={styles.tableRow}>{product.name}</td>
<td className={styles.tableRow}>{product.price}</td>
</tr>
);
}
return null;
}
return null;
})}
</table>
</div>
);
}
export default Products;
Filter.jsx:
import CategoryComponent from "./Category";
import styles from "../styles/filter.module.css";
function Filter({
filters,
dispatch,
category,
getfilterList: filterList,
setFilterState: setFilterList,
range: rangeState,
}) {
const [Category, setCategory] = category;
const [Range, setRange] = rangeState;
const handleCategoryChanged = (event, categ, key) => {
let list = filterList();
if (list[categ][key].includes(event.currentTarget.value)) {
list[categ][key] = list[categ][key].filter(
(val) => val !== event.currentTarget.value
);
} else {
list[categ][key].push(event.currentTarget.value);
}
setFilterList(list);
if (Category !== categ) {
setCategory(categ);
} else {
let flag = false;
Object.keys(filterList()[categ]).forEach((key) => {
const obj = filterList()[categ][key];
if (obj.length !== 0) flag = true;
});
if (!flag) {
setCategory("all");
}
}
};
const handleRangeChanged = (event) => {
setRange(event.currentTarget.value);
};
return (
<div className="filter-container">
<h1>{Category}</h1>
<CategoryComponent
filterList={filterList()}
setFilterList={setFilterList}
category={Category}
handleCategoryChanged={handleCategoryChanged}
categories={{
brand: ["lenovo", "msi", "asus"],
processor: ["intel core i3", "intel core i5", "intel core i7"],
}}
>
laptops
</CategoryComponent>
<CategoryComponent
filterList={filterList()}
setFilterList={setFilterList}
handleCategoryChanged={handleCategoryChanged}
category={Category}
categories={{
brand: ["samsung", "infinx", "oppo"],
ram: ["8gb", "4gb", "2gb"],
storage: ["64gb", "128gb", "256gb"],
}}
>
phones
</CategoryComponent>
<CategoryComponent
filterList={filterList()}
setFilterList={setFilterList}
category={Category}
handleCategoryChanged={handleCategoryChanged}
categories={{
type: ["keyboard", "mouse", "screen"],
}}
>
accessories
</CategoryComponent>
<div className={styles.priceRangeContainer}>
<input
type="range"
min="0"
max="9999"
className={styles.priceRange}
value={Range}
onChange={handleRangeChanged}
/>
<span>{Range}DT</span>
</div>
</div>
);
}
export default Filter;
Category.jsx
import React from "react";
import { useState, useEffect } from "react";
import styles from "../styles/category.module.css";
import arrow from "../img/angle-up-solid-svgrepo-com.svg";
export default function Category({
handleCategoryChanged,
children,
categories,
category,
filterList,
setFilterList,
}) {
const [hiddenClass, setHiddenClass] = useState(styles.hidden);
const handleClick = () => {
if (hiddenClass) setHiddenClass("");
else setHiddenClass(styles.hidden);
};
return (
<div>
<h5>{category}</h5>
<div onClick={handleClick} className={styles.categoryBox}>
{children} <img style={{ width: 15 }} src={arrow} alt="" />
</div>
<div className={styles.hiddenScroll + " " + hiddenClass}>
{Object.entries(categories).map(([key, value]) => {
return (
<div key={key}>
<h5>{key}:</h5>
<ul>
{value.map((val) => {
console.log(filterList);
return (
<li key={val}>
<input
type="checkbox"
name={val}
id={val}
value={val}
onChange={(e) =>
handleCategoryChanged(e, children, key)
}
/>
<label htmlFor={val}>{val}</label>
</li>
);
})}
</ul>
</div>
);
})}
</div>
</div>
);
}
The problem is when i run my program the state changes when i check an only one checkbox, but when i click on more checkboxes nothing changes.
EDIT:
my components hierarchy :
products.jsx
filter.jsx
Category.jsx
in products.jsx :
i have a state filterList that has all the filters i need.
const filter = {
laptops: {
brand: [],
processor: [],
},
phones: {
brand: [],
ram: [],
storage: [],
},
accessories: {
type: [],
},
};
const [filterList, setFilterList] = useState(filter);
in Filter.jsx:
i have state filterList that was passed as a prop from the Products component.
and a function that handles any checkbox change from it's child component.
const handleCategoryChanged = (event, categ, key) => {
let list = filterList();
//testing if the value is already in the filterList if true we remove it
if (list[categ][key].includes(event.currentTarget.value)) {
list[categ][key] = list[categ][key].filter(
(val) => val !== event.currentTarget.value
);
} else {
// else we add it to the list
list[categ][key].push(event.currentTarget.value);
}
setFilterList(list);
Category.jsx:
here i created all my checkboxes with the onChange event.
But the problem is when more than one checkbox gets changed, my filterList state does not get re rendered.
You missed the useState in your Filter.jsx
Replacing
const [Category, setCategory] = category;
const [Range, setRange] = rangeState;
With
const [Category, setCategory] = useState(category);
const [Range, setRange] = useState(rangeState);
A small working example could be found at code sandbox
⚠️ You did not provide the parent of Filter.jsx so in the example only the laptop brands are working
const btn_start = document.getElementById("start");
let container = document.getElementById("container");
let questionTag = document.getElementById("question");
let answerTag = document.getElementsByClassName("answer");
const nxt_question_btn = document.getElementById("next");
const end_quiz_btn = document.getElementById("end");
btn_start.addEventListener("click", startQuiz);
nxt_question_btn.addEventListener("click", nextQuestion);
let currentQuestionIndex = 0;
let myQuestions = [
{
question: "What's 2+2?",
answers: [
{ text: "4", correct: true },
{ text: "2", correct: false },
{ text: "10", correct: false },
{ text: "1", correct: false },
],
},
{
question: "What's 10+10?",
answers: [
{ text: "20", correct: true },
{ text: "2", correct: false },
{ text: "18", correct: false },
{ text: "0", correct: false },
],
},
{
question: "What's 30+30?",
answers: [
{ text: "60", correct: true },
{ text: "24", correct: false },
{ text: "100", correct: false },
{ text: "50", correct: false },
],
},
{
question: "What's 10+30?",
answers: [
{ text: "40", correct: true },
{ text: "44", correct: false },
{ text: "70", correct: false },
{ text: "10", correct: false },
],
},
];
function startQuiz() {
container.style.visibility = "visible";
btn_start.style.visibility = "hidden";
end.style.visibility = "hidden";
showQuestion(myQuestions[0]);
}
function showQuestion(questionAndAnswers) {
const shuffledAnswers = _.shuffle(questionAndAnswers.answers);
questionTag.innerText = questionAndAnswers.question;
shuffledAnswers.forEach(({ text, correct }, i) => {
answerTag[i].innerText = text;
answerTag[i].dataset.correct = correct;
});
}
document.querySelectorAll(".answer").forEach((answer) => {
answer.addEventListener("click", (event) => {
if (event.target.dataset) {
answer.style.border = "1px solid black";
}
});
});
function nextQuestion() {
const nextIndex = currentQuestionIndex + 1;
if (nextIndex <= myQuestions.length - 1) {
showQuestion(myQuestions[nextIndex]);
currentQuestionIndex = nextIndex;
} else {
end.style.visibility = "visible";
nxt_question_btn.style.visibility = "hidden";
}
}
<button id="start" type="button">Start quiz</button>
<div id="container">
<h2>Quiz</h2>
<div class="time">
<span>Time left:</span>
<p id="time"> 30</p>
</div>
<h3 id="question"></h3>
<div class="answers">
<button id="answer1" class="answer"></button>
<button id="answer2" class="answer"></button>
<button id="answer3" class="answer"></button>
<button id="answer4" class="answer"></button>
</div>
<button id="next" class="btns">Next Question</button>
<button id="end" class="btns">End Quiz</button>
</div>
In my Javascript quiz game, when an answer is selected, we load up another question with other answers but the problem that occurs is the selected answers get a black border but when the next question is loaded up, the border stays. How do I make it go back to normal when the next question is loaded?
You could use border: initial to revert is back to default.
The initial CSS keyword applies the initial (or default) value of a property to an element. It can be applied to any CSS property. This includes the CSS shorthand all, with which initial can be used to restore all CSS properties to their initial state.
In your code, you could use:
let buttons = document.querySelectorAll('.answer')
buttons.forEach(button => {
button.addEventListener('click', function() {
this.style.border = "1px solid black";
buttons.forEach(each => {
if (each != button) each.style.border = "initial"
})
})
})
<button id="answer1" class="answer"></button>
<button id="answer2" class="answer"></button>
<button id="answer3" class="answer"></button>
<button id="answer4" class="answer"></button>
Or if the style of border you doesn't satisfy with, you could define a new class and use toggle to add / remove the classList.
*Also, note that your code is currently displaying reference error
In your "next question" function, set the button's style properties to none. Then put a setTimeout function where you set the same element's style property to '' (empty string). This should reset it.
const btn_start = document.getElementById("start");
let container = document.getElementById("container");
let questionTag = document.getElementById("question");
let answerTag = document.getElementsByClassName("answer");
const nxt_question_btn = document.getElementById("next");
const end_quiz_btn = document.getElementById("end");
btn_start.addEventListener("click", startQuiz);
nxt_question_btn.addEventListener("click", nextQuestion);
let currentQuestionIndex = 0;
let myQuestions = [
{
question: "What's 2+2?",
answers: [
{ text: "4", correct: true },
{ text: "2", correct: false },
{ text: "10", correct: false },
{ text: "1", correct: false },
],
},
{
question: "What's 10+10?",
answers: [
{ text: "20", correct: true },
{ text: "2", correct: false },
{ text: "18", correct: false },
{ text: "0", correct: false },
],
},
{
question: "What's 30+30?",
answers: [
{ text: "60", correct: true },
{ text: "24", correct: false },
{ text: "100", correct: false },
{ text: "50", correct: false },
],
},
{
question: "What's 10+30?",
answers: [
{ text: "40", correct: true },
{ text: "44", correct: false },
{ text: "70", correct: false },
{ text: "10", correct: false },
],
},
];
function startQuiz() {
container.style.visibility = "visible";
btn_start.style.visibility = "hidden";
end.style.visibility = "hidden";
showQuestion(myQuestions[0]);
}
function showQuestion(questionAndAnswers) {
const shuffledAnswers = _.shuffle(questionAndAnswers.answers);
questionTag.innerText = questionAndAnswers.question;
shuffledAnswers.forEach(({ text, correct }, i) => {
answerTag[i].innerText = text;
answerTag[i].dataset.correct = correct;
});
}
document.querySelectorAll(".answer").forEach((answer) => {
answer.addEventListener("click", (event) => {
if (event.target.dataset) {
answer.style.border = "1px solid black";
}
});
});
function nextQuestion() {
document.querySelectorAll('.answer').forEach((answer) => {
answer.style.border = 'none';
setTimeout(function() {
answer.style.border = ''
})
})
const nextIndex = currentQuestionIndex + 1;
if (nextIndex <= myQuestions.length - 1) {
showQuestion(myQuestions[nextIndex]);
currentQuestionIndex = nextIndex;
} else {
end.style.visibility = "visible";
nxt_question_btn.style.visibility = "hidden";
}
}
<button id="start" type="button">Start quiz</button>
<div id="container">
<h2>Quiz</h2>
<div class="time">
<span>Time left:</span>
<p id="time"> 30</p>
</div>
<h3 id="question"></h3>
<div class="answers">
<button id="answer1" class="answer"></button>
<button id="answer2" class="answer"></button>
<button id="answer3" class="answer"></button>
<button id="answer4" class="answer"></button>
</div>
<button id="next" class="btns">Next Question</button>
<button id="end" class="btns">End Quiz</button>
</div>
I am trying to create a page to add a new user when click into a value on menu bar like an Add option that allows users to input a name, an office number, and a phone number
Here is my code:
let menu = ["View", "Add", "Verify", "Update", "Delete"];
let list = document.getElementById("menuList");
menu.forEach((item) => {
let li = document.createElement("li");
li.innerText = item;
list.appendChild(li);
});
let users = [
{ name: "Jan", id: "1", number: "111-111-111" },
{ name: "Juan", id: "2", number: "222-222-222" },
{ name: "Margie", id: "3", number: "333-333-333" },
{ name: "Sara", id: "4", number: "444-444-444" },
{ name: "Tyrell", id: "5", number: "555-555-555" },
];
var div = "<div class='infor'>";
for (var i = 0; i < users.length; i++) {
div += "<div class='user-informations'>";
div += "<p>" + users[i].name + "</p>";
div += "<p>" + users[i].id + "</p>";
div += "<p>" + users[i].number + "</p>";
div += "</div>";
}
div += "</div>";
document.getElementById("usersList").innerHTML = div;
<div class="contact-container">
<div class="navbar">
<ul id="menuList">
<img src="https://img.icons8.com/ios-filled/50/000000/contact-card.png"/>
</ul>
</div>
<div class="users" id="usersList">
</div>
</div>
my project:
Paying no attention to style or good software engineering practices:
let usersList = document.getElementById("usersList"),
addPage = document.getElementById("addPage");
const users = [
{ name: "Jan", id: "1", number: "111-111-111" },
{ name: "Juan", id: "2", number: "222-222-222" },
{ name: "Margie", id: "3", number: "333-333-333" },
{ name: "Sara", id: "4", number: "444-444-444" },
{ name: "Tyrell", id: "5", number: "555-555-555" },
];
function showUsers() {
usersList.innerHTML = "";
usersList.style.display = "inline";
addPage.style.display = "none";
users.forEach(u => {
const newUser = document.createElement("p");
newUser.innerHTML = `${u.id} ${u.name}<br/>${u.number}`;
usersList.appendChild(newUser);
})
}
function addOn() {
usersList.style.display = "none";
addPage.style.display = "inline";
}
function addUser() {
const id = document.getElementById("id").value;
const name = document.getElementById("name").value;
const number = document.getElementById("number").value;
users.unshift({ name: name, id: id, number: number});
showUsers();
}
showUsers();
.navbar { vertical-align: top; padding-right: 1rem; border-right:solid 1px red }
<div class="contact-container">
<table>
<tr>
<td class="navbar">
<ul id="menuList" style="cursor:pointer">
<img src="https://img.icons8.com/ios-filled/50/000000/contact-card.png" />
<li onclick="showUsers()">View</li>
<li onclick="addOn()">Add</li>
<li>...</li>
</ul>
</td>
<td>
<div class="users" id="usersList"></div>
<div id="addPage" style="display:none">
Id: <input id="id" size="1"/> Name: <input id="name" /><br/>
Number: <input id="number" /><br/>
<button onclick="addUser()">Add</button>
</div>
</td>
</tr>
</table>
</div>
It is not necessary to generate the menu list dynamically unless the menu is really dynamic.
Ask if you need explanation on any of the stuff above.
As the header describes, I would like to append input elements to some fieldset in my form.
I build a staged form with 3 fieldsets which appear only when "Next" button is clicked.
Now in fieldset number 3 I want to input elements based on keys I'm extracting from a external json object.
Data:
data: () => ({
currentPage: 0,
brand: '',
platform: '',
affiliate: '',
fieldsetThree: document.getElementById("fieldset3"),
}),
Fieldset(only appears when currentPage value is 2):
<div v-if="currentPage === 2">
<fieldset id="fieldset3">
<h2 class="fs-title">API Credentials</h2>
<h3 class="fs-subtitle">Step 3- Add any parameter for the integration</h3>
</fieldset>
</div>
Function to append the input fields after Stage 2 is done:
appendFields() {
let check = json[this.platform];
console.log(this.fieldsetThree);
for (const [key, value] of Object.entries(check)) {
let inputfield = document.createElement("input");
this.inputfield.setAttribute("type", "text");
this.inputfield.setAttribute("name",`${key}`);
this.inputfield.setAttribute("placeholder",`${key}`);
console.log("Input FIeld \n", this.inputfield)
this.fieldsetThree.appendChild(this.inputfield);
}
//Build it before.
},
The goal is to create an input field for every key in the JSON, I will add the json format for example:
"exmaple": {
"Username": "",
"Password": "",
"AffiliateID": "",
"GI": "",
"CI": "",
"freeTextArea": ""
},
FUll code(without Template):
export default {
data: () => ({
currentPage: 0,
brand: '',
platform: '',
affiliate: '',
}),
computed: {
platformData: function () {
return json[this.platform];
}
},
methods: {
isNextClicked() {
var nextStage = this.currentPage;
this.currentPage++;
console.log("CurrentPage =>", this.currentPage);
$("#progressbar li").eq($("fieldset").index(nextStage)).addClass("active");
return this.currentPage;
},
isPreviousClicked() {
this.currentPage--;
var previousStage = this.currentPage;
console.log("CurrentPage at decrease =>", this.currentPage);
$("#progressbar li").eq($("fieldset").index(previousStage)).removeClass("active");
return this.currentPage;
},
appendFields() {
// let check = json[this.platform];
// console.log(this.fieldsetThree);
// for (const [key, value] of Object.entries(check)) {
//
// let inputfield = document.createElement("input");
// this.inputfield.setAttribute("type", "text");
// this.inputfield.setAttribute("name",`${key}`);
// this.inputfield.setAttribute("placeholder",`${key}`);
// console.log("Input FIeld \n", this.inputfield)
// this.fieldsetThree.appendChild(this.inputfield);
// }
//Build it before.
},
nextPlusappend() {
//this.appendFields();
this.isNextClicked();
}
}
}
If you work out the structure you want to keep your data, then you don't need to mess with DOM elements: just add the JSON to the data() & let Vue do its thing.
So, if you take the JSON data structure as the base for the form fields & fieldsets, then you can just add it to the data item holding the form input fields:
const example = {
"Username": "",
"Password": "",
"AffiliateID": "",
"GI": "",
"CI": "",
"freeTextArea": ""
}
Vue.component("formFieldset", {
props: ["fields"],
methods: {
handleInput(val, key) {
this.$emit("update:fields", { key, val })
},
},
template: `
<div>
<label
v-for="(val, key) in fields"
:key="key"
>
{{ key }}:
<input
type="text"
:placeholder="key"
#input="(e) => handleInput(e.target.value, key)"
/>
</label>
<hr>
</div>
`
})
new Vue({
el: "#app",
data() {
return {
fieldsets: [
{
field1_1: "",
field1_2: "",
},
{
field2_1: "",
field2_2: "",
},
],
}
},
// just to add the items later to the data():
mounted() {
this.fieldsets.push(example)
},
methods: {
handleUpdateFields({ key, val, idx }) {
this.fieldsets[idx][key] = val
},
},
template: `
<div>
Putting out the data():<br />
{{ fieldsets }}
<hr>
<form>
<form-fieldset
v-for="(fieldset, i) in fieldsets"
:key="i"
:fields="fieldset"
#update:fields="(e) => handleUpdateFields({...e, idx: i})"
/>
</form>
</div>
`
})
label {
display: block;
padding: 8px 16px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
How can I let my To-do dynamic, with the possibility to add items trought a form?
This is is my list:
const initialData = {
tasks: {
'task-1': { id: 'task-1', content: 'Org.Internacionais' },
'task-2': { id: 'task-2', content: 'Ind.Farm.LTDA' },
'task-3': { id: 'task-3', content: 'Musc.Sound Live Cmp' },
},
columns: {
'column-1': {
id: 'column-1',
title: 'Cliente em Potencial',
taskIds: ['task-1', 'task-2', 'task-3'],
},
'column-2': {
id: 'column-2',
title: 'Dados Confirmados',
taskIds: [],
},
'column-3': {
id: 'column-3',
title: 'Reunião Agendada',
taskIds: [],
},
},
columnOrder: ['column-1', 'column-2', 'column-3'],
};
This list got fixed tasks, and I wanna let dynamic, adding tasks through a form, but i don't know how to do this!
Code:
import React from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';
import { DragDropContext } from 'react-beautiful-dnd';
import '#atlaskit/css-reset';
import './styles.css';
import Column from './column';
const initialData = {
tasks: {
'task-1': { id: 'task-1', content: 'Org.Internacionais' },
'task-2': { id: 'task-2', content: 'Ind.Farm.LTDA' },
'task-3': { id: 'task-3', content: 'Musc.Sound Live Cmp' },
},
columns: {
'column-1': {
id: 'column-1',
title: 'Cliente em Potencial',
taskIds: ['task-1', 'task-2', 'task-3'],
},
'column-2': {
id: 'column-2',
title: 'Dados Confirmados',
taskIds: [],
},
'column-3': {
id: 'column-3',
title: 'Reunião Agendada',
taskIds: [],
},
},
columnOrder: ['column-1', 'column-2', 'column-3'],
};
const Container = styled.div`
display: flex;
`;
class LeadsComponent extends React.Component {
state = initialData;
onDragEnd = result => {
const { destination, source, draggableId } = result;
if (!destination) {
return;
}
if (
destination.droppableId === source.droppableId &&
destination.index === source.index
) {
return;
}
const start = this.state.columns[source.droppableId];
const finish = this.state.columns[destination.droppableId];
if (start === finish) {
const newTaskIds = Array.from(start.taskIds);
newTaskIds.splice(source.index, 1);
newTaskIds.splice(destination.index, 0, draggableId);
const newColumn = {
...start,
taskIds: newTaskIds,
};
const newState = {
...this.state,
columns: {
...this.state.columns,
[newColumn.id]: newColumn,
},
};
this.setState(newState);
return;
}
const startTaskIds = Array.from(start.taskIds);
startTaskIds.splice(source.index, 1);
const newStart = {
...start,
taskIds: startTaskIds,
};
const finishTaskIds = Array.from(finish.taskIds);
finishTaskIds.splice(destination.index, 0, draggableId);
const newFinish = {
...finish,
taskIds: finishTaskIds,
};
const newState = {
...this.state,
columns: {
...this.state.columns,
[newStart.id]: newStart,
[newFinish.id]: newFinish,
},
};
if (newStart.id == 'column-1' && newFinish.id == 'column-3') {
return;
} else if (newStart.id == 'column-2' && newFinish.id == 'column-1') {
return;
} else if (newStart.id == 'column-3' && newFinish.id == 'column-2') {
return;
} else if (newStart.id == 'column-3' && newFinish.id == 'column-1') {
return;
} else {
this.setState(newState);
}
};
render() {
return (
<>
<DragDropContext onDragEnd={this.onDragEnd}>
<Container>
{this.state.columnOrder.map(columnId => {
const column = this.state.columns[columnId];
const tasks = column.taskIds.map(
taskId => this.state.tasks[taskId],
);
return <Column key={column.id} column={column} tasks={tasks} />;
})}
</Container>
</DragDropContext>
<button
type="button"
class="btn btn-primary"
data-toggle="modal"
data-target="#staticBackdrop"
>
Launch static backdrop modal
</button>
<div
class="modal fade"
id="staticBackdrop"
data-backdrop="static"
data-keyboard="false"
tabindex="-1"
aria-labelledby="staticBackdropLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">
Modal title
</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form>
<input type="text" id="content" className="teste" />
<input type="submit" value="Submit" />
</form>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-dismiss="modal"
>
Close
</button>
<button type="button" class="btn btn-primary">
Understood
</button>
</div>
</div>
</div>
</div>
</>
);
}
}
export default LeadsComponent;
On this code already has a form with one input and submit! Can you help me to solve this problem?
First, your state needs a counter field to create an index for new items and a text field to store the text for the new item.
const initialData = {
count: 3,
newTask: '',
tasks: {
'task-1': { id: 'task-1', content: 'Org.Internacionais' },
'task-2': { id: 'task-2', content: 'Ind.Farm.LTDA' },
'task-3': { id: 'task-3', content: 'Musc.Sound Live Cmp' },
},
Then define the handlers as described in the question I commented
inputChangeHandler = ({ target: { value } }) =>
this.setState({
newTask: value,
});
submitHandler = e => {
e.preventDefault();
this.setState(prevState => {
// increment task count
const newCount = prevState.count + 1;
// create new id based on task count
const newId = `task-${newCount}`;
return {
count: newCount,
// clear input
newTask: '',
tasks: {
// add to tasks array
...prevState.tasks,
[newId]: { id: newId, content: prevState.newTask },
},
// add task id at the end of first column
columns: {
...prevState.columns,
'column-1': {
...prevState.columns['column-1'],
taskIds: [...prevState.columns['column-1'].taskIds, newId],
},
},
};
});
};
And pass the handler to the form components
<form onSubmit={this.submitHandler}>
<input
type="text"
id="content"
className="teste"
value={this.state.newTask}
onChange={this.inputChangeHandler}
/>
<input type="submit" value="Submit" />
</form>;
You main concern would be the way you update your state since it's gotten complex