How can I move the selected list entry by clicking button and move it to a new list entry
<pre className="reverse-list">
{datas.map((data, i) =>
<li key={i} className="list-group-item text-capitalize d-flex
justify-content-between my-2">
<div className="todo-icon">
<span>
<button></button>
</span>
</div>
<h6 className="mt-2">{data.input}</h6>
<div className="todo-icon">
<span className="mx-2 text-success">
<i className="fas fa-pen" onClick={()=>this.fEdit(i)</i>
</span>
<span className="mx-2 text-danger">
<i className="fas fa-trash" onClick={()=>this.fRemove(i)</i>
</span>
</div>
</li>
)}
</pre>
Methods used
constructor(props){
super(props);
this.state={
title: 'Todo List',
act: 0,
index: '',
datas: []
}
}
componentDidMount(){
this.refs.input.focus();
}
What should i add in the fcomplete method to get the required output?
fSubmit = (e) =>{
e.preventDefault();
console.log('try');
let datas = this.state.datas;
let input = this.refs.input.value;
if(this.state.act === 0){ //new
let data = {
input
}
datas.push(data);
}else{ //update
let index = this.state.index;
datas[index].input = input;
}
this.setState({
datas: datas,
act: 0
});
this.refs.myForm.reset();
this.refs.input.focus();
}
fComplete = (i) => {
}
fRemove = (i) => {
let datas = this.state.datas;
datas.splice(i,1);
this.setState({
datas: datas
});
this.refs.myForm.reset();
this.refs.input.focus();
}
fEdit = (i) => {
let data = this.state.datas[i];
this.refs.input.value = data.input;
this.setState({
act: 1,
index: i
});
this.refs.input.focus();
}
I have tried using methods in fcomplete(). But I was not able to get the task done.I have added the complete code.
csccscsccs
csscscsccssc
If I understand you correctly, you want to display your list in such a way that the items marked as completed are displayed at the bottom of the list. My below answer is based on this assumption only.
Like I mentioned in the comments, you have to store your list in a different way:
fSubmit = (e) =>{
e.preventDefault();
console.log('try');
let datas = this.state.datas;
let input = this.refs.input.value;
if(this.state.act === 0){ //new
let data = {
id: (Math.floor(Math.random() * 100) + 1), // for generating random ids
value: input,
isComplete: false // you have to make this change here while creating the data item
}
datas.push(data);
}else{ //update
let index = this.state.index;
datas[index].input = input;
}
this.setState({
datas: datas,
act: 0
});
this.refs.myForm.reset();
this.refs.input.focus();
}
fComplete = (id) => {
// use id to update the list
// use this in fEdit & fRemove
const updatedDataList = this.state.datas.map(data => {
if(data.id === id) {
data.isComplete = true
}
return data;
});
this.setState({
datas: updatedDataList
});
}
render() {
const incompleteList = datas.filter(data => data.isComplete === false);
const completeList = datas.filter(data => data.isComplete === true);
...
...
...
}
Display the two lists separately. (I don't understand your css classes, that you can see according to your design.) The idea is to display them in two separate lists to make it simple. Or otherwise you have to always modify your "datas" list and keep the incomplete list on top and append the completed data items at the end.
<pre className="reverse-list">
{incompleteList.map((data, i) =>
<li key={i} className="list-group-item text-capitalize d-flex
justify-content-between my-2">
<div className="todo-icon">
<span>
<button onClick={() => this.fComplete({data.id})}></button>
</span>
</div>
<h6 className="mt-2">{data.input}</h6>
<div className="todo-icon">
<span className="mx-2 text-success">
<i className="fas fa-pen" onClick={()=>this.fEdit({data.id})</i>
</span>
<span className="mx-2 text-danger">
<i className="fas fa-trash" onClick={()=>this.fRemove({data.id})</i>
</span>
</div>
</li>
)}
{completeList.map((data, i) =>
<li key={i} className="list-group-item text-capitalize d-flex
justify-content-between my-2">
<div className="todo-icon">
<span>
<button></button>
</span>
</div>
<h6 className="mt-2">{data.input}</h6>
<div className="todo-icon">
<span className="mx-2 text-success">
<i className="fas fa-pen" onClick={()=>this.fEdit(i)</i>
</span>
<span className="mx-2 text-danger">
<i className="fas fa-trash" onClick={()=>this.fRemove(i)</i>
</span>
</div>
</li>
)}
</pre>
Related
Ok, so I am creating employers list with salary and Names. I want to change color of text by changing value of variable - "increase" from false to true for example. I already created code to change text taking value FROM variable
<li className={ increase ? "list-group-item d-flex justify-content-between increase" : "list-group-item d-flex justify-content-between"}>
Now I need to change value of "increase" by clicking a button <i className="fas fa-cookie"></i>
Here is a full code:
const EmployeesListItem = ({name, salary, increase}) => {
return (
//-------Here I check the value of variable----->
<li className={ increase ? "list-group-item d-flex justify-content-between increase" : "list-group-item d-flex justify-content-between"}>
<span className="list-group-item-label">{name}</span>
<input type="text" className="list-group-item-input" defaultValue={salary + "$"}/>
<div className='d-flex justify-content-center align-items-center'>
<button type="button"
className="btn-cookie btn-sm ">
<i className="fas fa-cookie"></i> //by clicking in this button I wanna change value of increase variable
</button>
<button type="button"
className="btn-trash btn-sm ">
<i className="fas fa-trash"></i>
</button>
<i className="fas fa-star"></i>
</div>
</li>
)
}
export default EmployeesListItem;
Here is EmployerList structure:
import EmployeesListItem from '../employeers-list-item/employeers-list-item'
import './employers-list.css'
const EmployersList = ({data}) => {
const elements = data.map(item => {
return(
<EmployeesListItem {...item} data={data}/> //name={item.name} salary={item.salary}
)
})
return (
<ul className='app-list list-group'>
{elements}
</ul>
)
}
export default EmployersList;
And here is App component:
function App() {
const data = [
{name: 'killer228brawlstarsassasin1993', salary:2300 ,increase: true},
{name: 'jotaro', salary:800 ,increase: false},
{name: 'Ktoh', salary:300 ,increase: true}
]
return (
<div className="app">
<AppInfo/>
<div className="search-panel">
<SearchPanel>
</SearchPanel>
<AppFilter/>
</div>
<EmployersList data={data}/>
<EmployeesAddForm/>
</div>
)
}
export default App;
the program works, but still don't know how to change the value of the INCREASE variable in the APP component by clicking on the button
You should read about React Context, this will help you to store data so it is accessible from every component: https://reactjs.org/docs/context.html
The way your app works now you need to pass a "changeIncrease"-function as prop to the EmployeesListItem component.
const changeIncrease = (name) => {
data.forEach(employee, index) => {
if(employee.name == name) {
data[index] = {...emloyee, increase: !employee.increase}
}
}
}
Maybe there's a more elegant solution out there, but this should do it
Here is what I have for my script :
function changeCart(subCat)
{
let addedCat = {id: subCat._id,name: subCat.name, name_categorie: subCat.name_categorie}
let exist = $cart.some(element => {
if(element.id === addedCat.id)
{
return true
}
})
if(exist)
{
$cart.some(element => {
if(element.id === addedCat.id)
{
$cart.splice($cart.indexOf(element, 1))
return true
}
})
}
else
{
$cart = [...$cart, addedCat]
}
console.log($cart)
}
and here is my html :
{#each subCategories as subCategories}
{#if ($cart.indexOf(subCategories.name) > -1)}
<div class="media ali list-group-item list-group-item-action clique selected" on:click={() => changeCart(subCategories)}>
<Management/>
<div class="media-body" >
<h4 class="media-heading">{subCategories.name}</h4>
</div>
</div>
{:else}
<div class="media ali list-group-item list-group-item-action clique" on:click={() => changeCart(subCategories)}>
<Management/>
<div class="media-body" >
<h4 class="media-heading">{subCategories.name}</h4>
</div>
</div>
{/if}
{/each}
I want to change this line :
{#if ($cart.indexOf(subCategories.name) > -1)}
The goal is to check if an object is already in $cart like the script part already do but I don't know how to make the modification for the if statement in my html
You can simply use the class:<name> Svelte directive. To add better readability, you could also make use of the new-ish #const block directive:
{#each subCategories as subCategory}
{#const selected = $cart.some(element => element.id === subCategory.id)}
<div class:selected class="media ali list-group-item list-group-item-action clique" on:click={() => changeCart(subCategory)}>
<Management/>
<div class="media-body" >
<h4 class="media-heading">{subCategory.name}</h4>
</div>
</div>
{/each}
Note: also changed the name of the currently iterated value to subCategory (don't name the iterator the same as the iteratee).
I found the solution : this is what I've done :
Script part :
function changeCart(subCat)
{
let addedCat = {id: subCat._id,name: subCat.name, name_categorie: subCat.name_categorie}
let exist = checkIfExist(subCat)
if(exist >= 0)
{
$cart = $cart.filter(item => item.id !== subCat._id)
}
else
{
$cart = [...$cart, addedCat]
}
console.log($cart)
}
function checkIfExist(cat)
{
for( let i = 0; i < $cart.length; i++)
{
if($cart[i].id == cat._id){
return i
}
}
return -1
}
and the html part :
{#key $cart}
{#each subCategories as subCategory}
<div class={`media ali list-group-item list-group-item-action ${checkIfExist(subCategory) >= 0 ? "selected" :""} clique`} on:click={() => {changeCart(subCategory)}}>
<Management/>
<div class="media-body">
<h4 class="media-heading">{subCategory.name}</h4>
</div>
</div>
{/each}
{/key}
I am trying to learn react js , but I don't know how to delete an item from my list, could you help me ? Actually, I am not professional , but I am really interested in learning, there is my codes which add some new item to my list properly, but I don't know how to delete them when I click on their checkbox/delete button.
import React, { useState } from "react";
const App = () => {
const [NewTaskText, setNewTaskText] = useState("");
const [Tasks, setTasks] = useState(["do a task", "samira"]);
const addTask = (e) => {
e.preventDefault();
if (!NewTaskText) return;
setTasks((currentTasks) => {
return [...currentTasks, NewTaskText];
});
setNewTaskText("");
};
const onchangeInput = (e) => {
const value = e.target.value;
setNewTaskText(value);
};
return (
<div className="row">
<form onSubmit={addTask}>
<div className="row col s6">
<div className="input-field col s10">
<textarea
id="textarea1"
className="materialize-textarea"
value={NewTaskText}
onChange={onchangeInput}
></textarea>
<label htmlFor="textarea1">What needs to be done ?</label>
</div>
</div>
<div className="row col s6">
<br></br>
<a className='waves-effect waves-light btn' href="/#" onClick={addTask}>
<i className='material-icons left' >
add_circle
</i>
Add
</a>
</div>
</form>
<div className="row">
<div className="row col s9">
<ul className="collection with-header">
<li className="collection-header">
<h4>Todo List</h4>
<form>
<div className="input-field">
<input id="search" type="search" required />
<label className="label-icon" htmlFor="search">
<i className="material-icons">search</i>Search
</label>
<i className="material-icons">close</i>
</div>
</form>
</li>
<label>
{Tasks.map((item, i) => {
return (
<li className="collection-item" key={i}>
<input type="checkbox" />
<span>
{item}
</span>
<span>
<a href="#!" className="secondary-content">
<i className="material-icons" >delete</i>
<i className="material-icons">check_circle</i>
</a>
</span>
</li>
);
})}
</label>
</ul>
</div>
</div>
</div>
);
};
export default App;
You can delete the item from the array by using index:
<i className="material-icons" onClick={() => handleDelete(i)}>delete</i>
and define your function above the return statement:
const handleDelete = i => {
const taskList = [...Tasks]
taskList.splice(i, 1)
setTasks(taskList)
}
Here it is important to know that you have to make a copy of the state if it is an object or an array. Because if you modify the original array or object, react won't register that as a change, and won't re-render. That is why I did const taskList = [...Tasks]. You can also use a library like lodash which provides some neat ways of handling objects and arrays
Just add a onClick handler to button which should delete item
<i className="material-icons" onClick={ () => {
setTasks((prevTasks) => {
const updatedPrevTasks = [...prevTasks];
updatedPrevTasks.splice(i, 1);
return updatedPrevTasks;
});
}}>delete</i>
const setupProducts = (data) => {
if (data.length) {
let html = '';
data.forEach(doc => {
const product = doc.data();
const li = `
<li>
<div class="collapsible-header grey lighten-4"> ${product.title} </div>
<div class="collapsible-header grey lighten-4"> ${doc.id} </div>
<div class="collapsible-body white"> ${product.content}
<a href="" class="secondary-content">
<a class="btn orange modal-trigger" >Get ID</a>
</a>
</li>
`;
html += li;
});
productList.innerHTML = html
} else {
productList.innerHTML = '<h5 class="center-align">Login to view products</h5>';
}
};
My idea is that I want to get the ID by clicking on the document and then but the product.title in db.collection('activeWorks').doc(doc.id or product.id (I don't know what's right...)).set. I have no idea how to do this, please help
Maybe do something like this: set the id tag of each <li> element to the doc id from Firestore, then attach an onclick event trigger
const setupProducts = (data) => {
if (data.length) {
let html = '';
productList.innerHTML = ''
data.forEach(doc => {
const product = doc.data();
const li = `
<li id="${doc.id}">
<div class="collapsible-header grey lighten-4"> ${product.title} </div>
<div class="collapsible-header grey lighten-4"> ${doc.id} </div>
<div class="collapsible-body white"> ${product.content}
<a href="" class="secondary-content">
<a class="btn orange modal-trigger" >Get ID</a>
</a>
</li>
`;
html += li;
productList.innerHTML += html
document.getElementById(doc.id).onclick = () => {
// do firestore stuff here
// db.collection('activeWorks').doc(doc.id).set(your_data)
}
});
} else {
productList.innerHTML = '<h5 class="center-align">Login to view products</h5>';
}
};
If I misunderstood your question I'm sorry
When I clicked on the button "Buy", the store redux is updated and
push a new array to the initial state. But I don't see any new render
in the shop-cart section without leaving the page and go in again so I
can see new items. I'm new to React and Redux, I have been trying to fix it but it was not working and i'm very confused about it
Here is the reducer section
const initialState = {
counter : 0,
cartData : [
{
id : 1,
name : "Melon",
price : 100,
img : "https://d1hr6nb56yyl1.cloudfront.net/product-images/93368-280.jpg",
quantity : 1
},
{
id : 1,
name : "Melon",
price : 100,
img : "https://d1hr6nb56yyl1.cloudfront.net/product-images/93368-280.jpg",
quantity : 1
},
]
};
function cartNumber(state = initialState, action) {
console.log(state.cartData)
let cartIndex = [...state.cartData];
switch(action.type){
case "INCREMENT" :
return {
...state,
cartData : cartIndex.concat(action.newData)
};
break;
case "DECREMENT" :
default :
return state;
}
return cartIndex
}
export default cartNumber;
Here is the Item Detail page and Items Cart section
import React, {useEffect,useState} from "react";
import SubCart from "../components/SubCart";
import { useSelector, useDispatch } from "react-redux";
import {Link} from "react-router-dom";
function ItemDetail({match, history}){
const dispatch = useDispatch();
const [item, setItem] = useState({});
const scroll = () => {window.scroll(0,0)};
useEffect(() => {
const fetchItem = async () => {
const fetchItem = await fetch(
`https://5e5a9fc26a71ea0014e61f04.mockapi.io/api/${match.params.category}/${match.params.id}`
);
const item = await fetchItem.json();
setItem(item);
};
fetchItem();
scroll();
}, [])
const showRating = (rating) => {
var result = [];
for(var i = 1; i <= rating; i++){
result.push(<i className="fa fa-star" key={i + 1} style={{fontSize : ".8rem", color : "#ff4e00", padding : "0rem .1rem"}}></i>);
}
for (var j = 1; j <= (5-rating); j++) {
result.push(<i className="far fa-star" key={i + 1} style={{fontSize : ".8rem", padding : "0rem .1rem"}}></i>);
}
return result;
}
const backButton = () => {
history.goBack();
}
const showSubcart = () => {
let sub = document.getElementById("subcart-section");
let bg = document.getElementById("bg-ccc");
sub.classList.toggle("showSubCart");
bg.classList.toggle("bg-ccc");
dispatch({type : "INCREMENT", newData : item });
}
return (
<div style={{backgroundColor: "white"}}>
<div className="item-detail" style={{paddingBottom : "1rem"}}>
<div className="section-heading">
<div className="section-content">
<div className="icon-home">
<i className="fas fa-home"></i>
</div>
<div className="nav-home">
<Link to="/">Home</Link>
</div>
<div className="icon-to">
<i className="fas fa-chevron-right"></i>
</div>
<div className="nav-cart">
<Link to="/">{item.category}</Link>
</div>
</div>
</div>
<div className="img-detail">
<img src={item.img} alt={item.name} />
<span className="icon-section"><i className="far fa-heart"></i></span>
<span className="icon-section-arrow" onClick={() => backButton()}><i className="fas fa-arrow-left"></i></span>
</div>
<div className="item-detail-content">
<div className="container">
<Link to="/" style={{color : "black"}}><span className="category">{item.category}</span></Link>
<div><h2>{item.name}</h2></div>
<div><h3>{item.price}$</h3></div>
<div>
<ul>
<li>{showRating(item.rating)} <span style={{fontSize : ".8rem"}}>(210)</span></li>
</ul>
</div>
<div className="buy" onClick={() => showSubcart()}>
<span className="buy-text">Buy</span>
<span className="buy-icon"><i className="fas fa-shopping-cart"></i></span>
</div>
<div className="description">
<div className="head-description">
<h3><i className="far fa-bookmark"></i> Description</h3>
</div>
<div className="body-description">
<p>...</p>
</div>
</div>
<div className="infoPolicy mt-top">
<div className="box-head">
<i className="fas fa-truck"></i>
</div>
<div className="box-content">
<p className="note-p">FREE NATIONAL DELIVERY</p><span className="note">(Products over 50$)</span>
</div>
</div>
<div className="infoPolicy mt-top">
<div className="box-head">
<i className="fas fa-sync-alt"></i>
</div>
<div className="box-content">
<p className="note-p">Returns are Easy</p><span className="note">(Return 30days for items)</span>
</div>
</div>
<div className="infoPolicy mt-top mt-bt">
<div className="box-head">
<i className="fas fa-headset"></i>
</div>
<div className="box-content">
<p className="note-p">Telephone consultation</p><span className="note">(Free from 8:00 - 21:00 every day)</span>
</div>
</div>
</div>
</div>
<SubCart />
</div>
</div>
);
}
export default ItemDetail;
import React, {useState} from "react";
import {Link} from "react-router-dom";
import {useSelector} from "react-redux";
function ItemCart(){
let data = useSelector(state => state.cartData);
let [items, setItem] = useState(data);
return (
<div className="subcart-link">
{items.map((item, index) => (
<div className="d-flex" key={index}>
<div className="subcart-img">
<img src={item.img} alt="pubg"/>
</div>
<div className="subcart-item-content">
<Link to="/">{item.name}</Link>
<div className="subcart-item-detail">
<p>{item.price}$<span className="subcart-quantity">x 2</span></p>
</div>
</div>
<div className="delete-click">
<p>x</p>
</div>
</div>
))}
</div>
);
}
at your ItemCart you are duplicating redux state into a react state, which is not advisable. they are two different entities, though you can have both types at your application a given specific state should be handled by a specific state manager.
at your ItemCart remove useState altogether, and map over data variable instead. your issue comes from the fact that your items state inherits only the original redux state, but since they are different states, it doesnt get updated when redux state changes.
fwiw, dont use dom manipulation to handle classes like you do at showSubCart, this is not good. I suggest to use some boolean logic instead to toggle class, based on some props or state value.