I'm working on a project named "Food Recipes", where a user can create, edit, delete his own recipes.
When creating a new recipe, the user must select the ingrediets.
So here is where I need your help:
By an axios call, I'm getting all the ingredients and show them into a table. The table looks like this:
|---------------------|------------------|-----------------|
| Ingredient | Check | Amount |
|---------------------|------------------|-----------------|
| Tomato | true | 2 kg |
|---------------------|------------------|-----------------|
| Potato | false | |
|---------------------|------------------|-----------------|
| Onion | true | 1 kg |
|---------------------|------------------|-----------------|
After checking some ingredient to TRUE, I want to have onChange function that will create list of IDs, from all ingredients that are CHECKED (in this case, I will have list with two elements: ID from Tomato, and ID from Onion)
To mention, i put ingredient ID as a value inside the <input type="checkbox"/>
Here is my code:
import React, {Component, useEffect, useState} from 'react'
import axios from "../../../axios/axios"
class Ingredient extends Component {
constructor(props){
super(props)
this.state = {
ingredients: [],
ingedientsList: [],
isChecked: false,
}
}
onIngredientChange = (e) => {
//HERE
};
componentDidMount() {
axios.get("/ingredients").then((data) => {
const ingredients = Object.keys(data.data).map((ingredient, index) => {
return (
<tr key={index}>
<td scope="col">
<label>{data.data[index].name}</label>
</td>
<td scope="col">
<input
id={data.data[index].id}
key={index}
type="checkbox"
name={"newIngredients"}
value={data.data[index].id}
onChange={this.onIngredientChange}
/>
</td>
<td scope="col">
<input
id={data.data[index].id + "amount"}
key={index}
type="number"
min="0"
max="500"
name="amount"
placeholder="grams"
onChange={this.onIngredientChange}
/>
</td>
</tr>
);
});
this.setState({ingredients: ingredients});
});
}
render() {
return (
<div className="form-group">
<table className="table tr-history table-striped small">
<thead>
<tr>
<th scope="col">
<h5>Ingredient</h5>
</th>
<th scope="col">
<h5>Check</h5>
</th>
<th scope="col">
<h5>Amount</h5>
</th>
</tr>
</thead>
<tbody>
{this.state.ingredients}
</tbody>
</table>
</div>
)
}
}
export default Ingredient;
Update
Try the code below.
const data = {
data:[
{
id:1,
name:"A"
},
{
id:2,
name:"B"
},
{
id:3,
name:"C"
}
]
}
class Ingredient extends React.Component {
constructor(props){
super(props)
this.state = {
ingredients: [],
ingedientsList: [],
checkedList:[],
isChecked: false,
}
}
componentDidMount() {
const ingredients = data.data.map((ingredient, index) => {
return (
<tr key={index}>
<td scope="col">
<label>{data.data[index].name}</label>
</td>
<td scope="col">
<input
id={data.data[index].id}
key={index}
type="checkbox"
name={"newIngredients"}
value={data.data[index].id}
onChange={(e)=>this.onIngredientChange(e,data.data[index].id)}
/>
</td>
<td scope="col">
<input
id={data.data[index].id + "amount"}
key={index}
type="number"
min="0"
max="500"
name="amount"
placeholder="grams"
onChange={this.onIngredientChange}
/>
</td>
</tr>
);
});
this.setState({ingredients: ingredients});
}
onIngredientChange = (e,id) => {
let resultArray = []
if(e.target.checked) //if checked (true), then add this id into checkedList
{
resultArray = this.state.checkedList.filter(CheckedId=>
CheckedId !== id
)
resultArray.push(id)
}
else //if not checked (false), then remove this id from checkedList
{
resultArray = this.state.checkedList.filter(CheckedId=>
CheckedId !== id
)
}
console.log(resultArray)
this.setState({
checkedList:resultArray
})
}
render() {
return (
<div className="form-group">
<table className="table tr-history table-striped small">
<thead>
<tr>
<th scope="col">
<h5>Ingredient</h5>
</th>
<th scope="col">
<h5>Check</h5>
</th>
<th scope="col">
<h5>Amount</h5>
</th>
</tr>
</thead>
<tbody>
{this.state.ingredients}
</tbody>
</table>
</div>
)
}
}
// Render it
ReactDOM.render(
<Ingredient />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
First, have a checked list in state
this.state = {
ingredients: [],
ingedientsList: [],
checkedList:[]
isChecked: false,
}
Then change element a little bit,
<input
id={data.data[index].id}
key={index}
type="checkbox"
name={"newIngredients"}
value={data.data[index].id}
onChange={(e)=>this.onIngredientChange(e,data.data[index].id)}
/>
Then change handler,
onIngredientChange = (e,id) => {
let resultArray = []
if(e.target.checked) //if checked (true), then add this id into checkedList
{
resultArray = this.state.checkedList.filter(CheckedId=>
CheckedId !== id // filter(checkID=>{CheckedId !== id}) remove {}
)
resultArray.push(id)
}
else //if not checked (false), then remove this id from checkedList
{
resultArray = this.state.checkedList.filter(CheckedId=>
CheckedId !== id // filter(checkID=>{CheckedId !== id}) remove {}
)
}
this.setState({
checkedList:resultArray
})
console.log(resultArray) // get all checked ID
};
Try this. Use a state array instead of simple array.
onIngredientChange = (e, value) => {
if (array.includes(value) && !e.target.checked) {
array.splice( array.indexOf(value), 1 );
} else if (!array.includes(value) && e.target.checked) {
array.push(value)
}
};
<input id={data.data[index].id} key={index}
type="checkbox" name={"newIngredients"}
value={data.data[index].id}
onChange={(e) =>this.onIngredientChange(e, value)} />
...
onIngredientChange = e => {
const checkedIngrediants = {
...this.state.checkedIngrediants
[e.target.id]: !e.target.value
};
this.setState({
checkedIngredients,
ingredientsList: Object.keys(checkedIngredients)
.filter(key => checkedIngredients[key])
})
}
...
<input
id={data.data[index].id}
key={index}
type="checkbox"
name={"newIngredients"}
value={this.state.checkedIngrediants[data.data[index].id]}
onChange={this.onIngredientChange} />
Related
I'm trying to get only checked items saved in an array So that I'm going to export it as a CSV file. need help, that's my sandbox below;
https://codesandbox.io/s/laughing-bush-5fvl7b?file=/src/App.js
You can save the selected items id in state. Have a look:
import { useState } from "react";
import { CategoriesData } from "../data";
import "./styles.css";
export default function App() {
const [selectedIds, setSelectedIds] = useState([]);
return (
<div className="App">
<table id="categories">
<thead>
<tr id="titres">
<td className="input-check">
<input type="checkbox" className="checkbox" />
</td>
<td>CATEGORIES</td>
<td>DESCRIPTION</td>
</tr>
</thead>
<tbody>
{CategoriesData.map((category, index) => (
<tr
id={`element-${index}`}
key={category._id}
className={
selectedIds.includes(category.id) ? "product active" : "product"
}
>
<td className="input-check">
<input
type="checkbox"
className="checkbox"
onClick={(e) => {
var selected = e.target.checked;
if (selected) {
setSelectedIds([...selectedIds, category.id]);
} else {
setSelectedIds([
...selectedIds.filter((x) => x !== category.id)
]);
}
}}
/>
</td>
<td>{category.designation}</td>
<td id="category-desc">{category.description}</td>
</tr>
))}
</tbody>
</table>
</div>
I am trying to map out a number of elements in a table and each of the elements has a specific value. Whenever i use value={inputArrival} or value={inputBurst} all the values of the specific column get update. I am attaching a screenshot. How to fix this?
Screenshot of what's happening
import React,{useState} from 'react';
import './tableEdit.css';
const EntryTable = (props) => {
const entry=props.numOfEntries;
const[inputArrival,SetinputArrival]=useState('');
const[inputBurst,SetinputBurst]=useState('');
function changeArrival(e)
{
console.log(e.target.value+"A");
/*
if(!Number(e.target.value))
return;
else*/
SetinputArrival(e.target.value);
}
function changeBurst(e)
{
console.log(e.target.value+"B");
SetinputBurst(e.target.value);
}
// console.log(entry);
var ArrayEntry=[];
for(var i=1;i<=entry;i++)
ArrayEntry.push( <tr key={i}>
<td className='row_editContent' >P{i}</td>
<td className='row_editContent'><input className='input_edit' placeholder='0' type='number' value={inputArrival} onChange={(e)=>changeArrival(e)}></input>ms</td>
<td className='row_editContent'><input className='input_edit' placeholder='0' type='number' value={inputBurst} onChange={(e)=>changeBurst(e)}></input>ms</td>
</tr>);
return (
<div>
<table className='MainTableContainer' >
<thead>
<tr >
<th className='row_editHeading'>Process</th>
<th className='row_editHeading'>Arrival Time</th>
<th className='row_editHeading'>Burst Time</th>
</tr>
</thead>
<tbody>
{ArrayEntry}
</tbody>
</table>
</div>
)
}
export default EntryTable
You are using the same single states inputArrival and inputBurst for every row entry of data in the table. Update so each is an array of strings, and update the changeArrival and changeBurst callbacks to curry an index to update. Use the mapped index to access the correct state and pass to the onChange callbacks.
const EntryTable = (props) => {
const entry = props.numOfEntries;
const [inputArrival, setInputArrival] = useState(Array(entry).fill(""));
const [inputBurst, setInputBurst] = useState(Array(entry).fill(""));
function changeArrival(index) {
return (e) => {
console.log(e.target.value + "A");
setInputArrival((values) =>
values.map((value, i) => (i === index ? e.target.value : value))
);
};
}
function changeBurst(index) {
return (e) => {
console.log(e.target.value + "B");
setInputBurst((values) =>
values.map((value, i) => (i === index ? e.target.value : value))
);
};
}
// console.log(entry);
const ArrayEntry = Array.from({ length: entry}).map((_, i) => (
<tr key={i}>
<td className="row_editContent">P{i}</td>
<td className="row_editContent">
<input
className="input_edit"
placeholder="0"
type="number"
value={inputArrival[i]}
onChange={changeArrival(i)}
/>
ms
</td>
<td className="row_editContent">
<input
className="input_edit"
placeholder="0"
type="number"
value={inputBurst[i]}
onChange={changeBurst(i)}
/>
ms
</td>
</tr>
));
return (
<div>
<table className="MainTableContainer">
<thead>
<tr>
<th className="row_editHeading">Process</th>
<th className="row_editHeading">Arrival Time</th>
<th className="row_editHeading">Burst Time</th>
</tr>
</thead>
<tbody>{ArrayEntry}</tbody>
</table>
</div>
);
};
I am new to react. I am using an addrow method to add the values to the table I created using {this.state.rows.map}. The table appears with the values of the input fields. However on pressing the continue button to go to the next page and then coming back from the page by pressing the back button, the row disappears.
Here is the code:
<Grid item xs={10} direction="row" alignItems="center">
<table
className="table table-bordered table-hover"
id="tab_logic"
>
<thead>
{this.state.rows.length > 0 ? (
<tr>
<th className="text-center"> # </th>
<th className="text-center"> KPI </th>
<th className="text-center"> UOM </th>
<th className="text-center"> Base </th>
<th className="text-center"> Target </th>
<th className="text-center"> Target Date </th>
<th className="text-center"> Delete Row </th>
</tr>
) : null}
</thead>
<tbody>
{this.state.rows.map((item, idx) => (
<tr id="addr1" key={idx}>
<td>{idx + 1}</td>
<td>
<input
type="text"
name="Kpi_Before"
defaultValue={Kpi_Before}
onChange={this.handleChangeRows(idx)}
className="form-control"
readOnly
/>
</td>
<td>
<input
type="text"
name="UOM_Before"
defaultValue={UOM_Before}
onChange={this.handleChangeRows(idx)}
className="form-control"
readOnly
/>
</td>
<td>
<input
type="text"
name="Base_Before"
defaultValue={Base_Before}
onChange={this.handleChangeRows(idx)}
className="form-control"
readOnly
/>
</td>
<td>
<input
type="text"
name="Target_Before"
defaultValue={Target_Before}
onChange={this.handleChangeRows(idx)}
className="form-control"
readOnly
/>
</td>
<td>
<input
type="text"
name="dateTime"
defaultValue={dateTime}
onChange={this.handleChangeRows(idx)}
className="form-control"
readOnly
size="38"
/>
</td>
<td>
The code for changes and going back and forward is this:
continue = e => {
e.preventDefault();
this.props.nextStep();
}
};
back = e => {
e.preventDefault();
this.props.prevStep();
};
handleChangeRows = idx => e => {
const { name, value } = e.target;
const rows = [...this.state.rows];
rows[idx] = {
[name]: value
};
this.setState({
rows
});
};
handleAddRow = () => {const item = {
Kpi_Before: [],
UOM_Before: "",
Base_Before: "",
Target_Before: "",
dateTime: "",
rows:[]
};
this.setState({
rows: [...this.state.rows, item]
});
}
};
nextStep = () => {
const { step } = this.state;
this.setState({
step: step + 1
});
};
//Go back to previous step
prevStep = () => {
const { step } = this.state;
this.setState({
step: step - 1
});
};
How do I keep the rows from disappearing and keep it constant on moving back and forward?
Your state re-renders when you navigate between pages or refresh which means everything inside state is lost to combat this you can either:
1) Use redux to store changes and retrieve data from there.
2) Use Context API which is the same as redux but builtin to react.
3) Consume a REST API which means have some sort of backend to store the values and get them on component mount.
i am trying to select all rows Id that contains in html table by one click i tried to do this but that function does not work and array list shows empty how to do that i can select all rows id by single checkbox and i want all rows id in selectedItems as list form . here is my code that i did.
this.state = {
checkedBoxCheck: false,
selectedItems: [],
stats: [],
}
componentDidMount() {
...
}
toggleSelectAll() {
let selectedItems = [];
if (this.state.selectAll === 0) {
this.state.stats.forEach(x => {
selectedItems[x.id] = true;
});
}
this.setState({
selectedItems: selectedItems,
checkedBoxCheck: true
});
console.log(selectedItems);
}
<thead>
<tr style={{ marginTop: "-88px" }}>
<th class="active" style={{ width: "20px" }}>
<input
type="checkbox"
class="select-all checkbox"
onChange={this.onChange}
name="first_name"
onClick={this.toggleSelectAll.bind(this)}
/>
</th>
<th className="" style={{ width: "20px" }}>
{t("Id")}
</th>
</thead>
<tbody>
{stats.map((c, i) => (
<tr key={c.id}>
<td>
<input
type="checkbox"
id="togBtn"
className="checkbox"
name="selectOptions"
onClick={() => this.onItemSelect(c.id)}
/>
</td>
<td>{c.id}</td>
</tbody>
I am building web application using railway api, on submitting train number I am trying to display data but getting the above error. Actually I used if else condition on fetching data.
below is the code.
import React, { Component } from "react";
export default class TrainRoute extends Component {
constructor(props) {
super(props);
this.state = { trainNumber: "", trainRouteList: "" };
this.onChange = this.onChange.bind(this);
this.onSubmitForm = this.onSubmitForm.bind(this);
}
onChange(e) {
this.setState({ [e.target.id]: e.target.value } );
}
onSubmitForm(e) {
e.preventDefault();
fetch(
`https://api.railwayapi.com/v2/route/train/${
this.state.trainNumber
}/apikey/sch9lj34uy/`
)
.then(res => res.json())
.then(data => {
this.setState({ trainRouteList: data }, () =>
console.log(this.state.trainRouteList)
);
});
this.setState({ trainNumber: "" });
}
render() {
const { trainRouteList } = this.state;
if (!trainRouteList) {
const list = <div>No Trains Details to display</div>;
const route = <div>No Routes to display</div>;
} else {
const list = (
<div>
<table>
<tbody>
<tr>
<td>Train Name :</td>
<td> {trainRouteList.train.name} </td>
</tr>
<tr>
<td>Train Number :</td>
<td> {trainRouteList.train.number} </td>
</tr>
<tr>
<td>Class :</td>
<td>
{trainRouteList.train.classes.map(trainClass => (
<span key={trainClass.code}>{trainClass.code},</span>
))}{" "}
</td>
</tr>
</tbody>
</table>
</div>
);
const route = trainRouteList.route.map(routeInfo => (
<table>
<tbody>
<tr>
<td>Station :</td>
<td> {routeInfo.station.name} </td>
</tr>
<tr>
<td>Departure Time :</td>
<td> {routeInfo.schdep} </td>
</tr>
<tr>
<td>Arrival Time :</td>
<td> {routeInfo.scharr} </td>
</tr>
</tbody>
</table>
));
}
return (
<div>
<div className="container">
<form
onSubmit={this.onSubmitForm}
className="col-md-8 col-md-offset-4"
>
<div className="row">
<div className="col-md-3">
<input
type="number"
className="form-control input-lg"
placeholder="Train Number"
id="trainNumber"
value={this.state.trainNumber}
onChange={this.onChange}
/>
</div>
<div className="col-md-1">
<button type="submit" className="btn btn-warning btn-lg">
Check Route
</button>
</div>
</div>
</form>
</div>
<div className="card card-fluid">
<div className="container">
{list}
<h3>Train Route</h3>
{route}
</div>
</div>
</div>
);
}
}
Getting error at list and route in render method. Can Anyone tell
me why I am getting this error as I tried all possible solution
Its because list and route are block level variable and you are trying to access them outside of block.
Solution is, Define those variables outside then update the value in if and else block, by that way we can access anywhere inside render method. Like this:
render() {
const { trainRouteList } = this.state;
let list, route;
if (!trainRouteList) {
list = <div>No Trains Details to display</div>;
route = <div>No Routes to display</div>;
} else {
list = ....;
route = ....;
}