Hi All i am building a clone service desk system using React and materialUI I am having issues with removing specific items from an array of objects when the user clicks on it. I have tried to use updatedRow.splice(index,1) but this just removes the last object added to the array and not a specific object. I am trying to remove it based on the ticketID property I have tried to use the array method indexof to console log the specific index of the object but it just returns -1 meaning the item is not in the array when its displaying on the screen. The function should filter and only keep the items which havent been selected based on if the condition is true and remove what is true then should call Setrows to update what is on the screen. Could someone explain exactly what I am doing wrong here see code below...
/// original array to populated with data
let row = [];
const [rows, setRows] = useState(row);
const formDataHandler = ({ desc, option }) => {
const data = {
description: desc,
priority: option,
lastUpdate: Date.now(),
ticketID:shortid.generate()
};
setRows([...rows, data]);
console.log(rows);
};
/// delete row function
const removeTicket = (index)=> {
let updatedRow = rows;
// updatedRow.splice(index,1)
console.log(updatedRow.filter(index => ticketID !== index.id? ))
setRows([...updatedRow])
/// returned
<Container maxWidth="md">
<Grid>
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Description</TableCell>
<TableCell>Priority</TableCell>
<TableCell>Last update</TableCell>
<TableCell>Ticket ID</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.length>0?rows.map((row) => (
<TableRow key={row.ticketID}>
<TableCell component="th" scope="row">
{row.description}
</TableCell>
<TableCell>{row.priority}</TableCell>
<TableCell>{row.lastUpdate}</TableCell>
<TableCell>{row.ticketID}</TableCell>
<TableCell>
<IconButton onClick={removeTicket} aria-label="delete" color="primary">
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
)):null}
</TableBody>
</Table>
</TableContainer>
</Grid>
<FormDialog formData={formDataHandler} />
{JSON.stringify(rows)}
</Container>
);
}
You need to pass the ticketID to your IconButton's onClick handler:
<IconButton
onClick={() => removeTicket(row.ticketID)}
aria-label="delete"
color="primary"
>
<DeleteIcon />
</IconButton>
And update your handler where you can create a new array using the .filter() method:
const removeTicket = (ticketID)=> {
setRows(rows.filter(item => ticketID !== item.ticketID)
}
This rows.filter() returns every element where the ticketID is not the same as the one you passed down.
If you know index that will be removed you can use Array.prototype.slice and
the logic is disconsider the element at index.
So, you will have to add the index as second param in your map function and change the onClick on IconButton to () => removeTicket(index).
Your remove function will contains the following snippet:
setRows(prevRows => [
...prevRows.slice(0, index),
...prevRows.slice(index + 1)
]);
If you need the ticketId to make a call to an api you can get it through index rows[index] or by passing it in the onClick function () => removeTicket(row.ticketID, index)
Related
The problem I am facing is really annoying, when I try to edit the default value of my input text, the input field won't type my changes or it would lose focus only after typing the first character and it will return to it's default value. But please note that this only happens when I add the onChange event. If I remove it, the input works normally. What is happening
const AddMaker = () => {
const [make, setMake] = useState();
function ConditionalTableRow(props) {
let { item, index } = props;
if ( index === editModeIndex)
return (<TableRowWithSave item={item} index={index} />)
else
return (<TableRowWithEdit item={item} index={index} />)
}
function TableRowWithSave(props) {
let { item } = props;
return (
<TableRow>
<TableCell>
<input type="text" defaultValue={item.make} onChange={e => setMake(e.target.value)} />
</TableCell>
<TableCell>
<button className="palletSaveButton" onClick={handleSaveClick}> Save </button>
</TableCell>
</TableRow>
)
}
return (
<TableBody>
{
records.map((item, index) => (
<ConditionalTableRow key={index} item={item} index={index} />
))
}
</TableBody>
);
}
Move useState to TableRowWithSave and use value instead of defaultValue
function ConditionalTableRow(props) {
let { item, index } = props;
if ( index === editModeIndex)
return (<TableRowWithSave item={item} index={index} />)
else
return (<TableRowWithEdit item={item} index={index} />)
}
function TableRowWithSave(props) {
let { item } = props;
const [make, setMake] = useState(item.make);
return (
<TableRow>
<TableCell>
<input type="text" value={make} onChange={e => setMake(e.target.value)} />
</TableCell>
<TableCell>
<button className="palletSaveButton" onClick={handleSaveClick}> Save </button>
</TableCell>
</TableRow>
)
}
const AddMaker = () => {
return (
<TableBody>
{
records.map((item, index) => (
<ConditionalTableRow key={index} item={item} index={index} />
))
}
</TableBody>
);
}
EDIT: Also it's better to move ConditionalTableRow and TableRowWithSave outside AddMaker
Try adding a value attribute to the input element.
<input type="text" value={make} defaultValue={item.make} onChange={e => setMake(e.target.value)} />
The short answer is possible because with each key stroke you are causeing a rerender with the onChange event.
This is a great question, and I had the same problem which was 3 parts.
RandomGenerated keys.
Wrong event type.
wrong react JSX attribute.
Keys: when you use random keys each rerender causes react to lose focus (key={Math.random()*36.4621596072}).
EventTypes: onChange cause a rerender with each key stroke, but this can also cause problems. onBlur is better because it updates after you click outside the input. An input, unless you want to "bind" it to something on the screen (visual builders), should use the onBlur event.
Attributes: JSX is not HTML and has it's own attributes (className,...). Instead of using value, it is better to use defaultValue={foo} in an input.
Once I changed these 3 things it worked great. Example below.
Parent:
const [near, setNear] = useState( "" );
const [location, setLocation] = useState( "" );
<ExperienceFormWhere
slug={slug}
questionWhere={question_where}
setLocation={handleChangeSetLocation}
locationState={location}
setNear={setNear}
nearState={near}
key={36.4621596072}/>
Child:
<input
defaultValue={locationState}
className={slug+"_question_where_select search_a_location"}
onBlur={event => setLocation(event.target.value)}/>
a great resource for form building is:
https://react-hook-form.com/
I'm using a axios to get my api to display some data from it. This works fine.
I want to get each of value and display the returned data when I click "TableRow"
this is my json data.
I want to get id and use axios api like this.
const toDetails = (e) => {
e.preventDefault();
const getDetails = async () => {
const response = await axios.get(`api/firstmemory/${id}`);
setUserData(response.data.data);
}
getDetails();
}
inside return
return(
<TableContainer component={Paper}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>things</TableCell>
<TableCell >date</TableCell>
</TableRow>
</TableHead>
<TableBody>
{userData.map((row,index) => (
<TableRow key={index}>
<TableCell>{row.first}</TableCell>
<TableCell>{row.date}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
Can anyone help me to figure this out ? Thank you.
You can add onClickto handle the click on the row and data-id attribute to store the id value on the row.
<TableRow key={index} onClick={handleRowClick} data-id={row.id}>
<TableCell>{row.first}</TableCell>
<TableCell>{row.date}</TableCell>
</TableRow>
Then you can read the data-id attribute's value in the click handler with
function handleRowClick(e) {
let id = e.currentTarget.getAttribute('data-id')
// Your axios code here
}
I am using map to loop over the array object getting from server, each object is used for one row in a table to show data. And I want to do a particular action for each row by calling function and pass to an index.
The code here:
<TableBody>
{productsData.map((product, index) => {
return (
<TableRow key={product.productId}>
<TableCell>
<Button
aria-owns={anchorEl ? `manipulation${index}` : undefined}
aria-haspopup="true"
onClick={handleClick}
className={classes.button}
size="small"
variant="contained"
>
Thao tác
</Button>
<Menu id={`manipulation${index}`} anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleClose}>
<MenuItem onClick={**handleOpen(index)**}>Xem trước</MenuItem>
</Menu>
</TableCell>
</TableRow>
)
})}
</TableBody>
The way I declare handleOpen: const handleOpen = (index) => () => {...}
=> I expected the handleOpen will render like that: handleOpen(0) for row 0, handleOpen(1) for row 1. But it's always end up with the last index of array. May be about the closure in javascript but I dont know how to fix
Please give me any suggestion. Thank in advance.
The way you've done it will call the handleOpen function immediately after rendering, instead of only calling it on click like you want it to.
To fix this, use an anonymous function:
<MenuItem onClick={() => handleOpen(index)}>
This will create a function that will only be called on actual click of the MenuItem component.
I am getting a payload and trying to map the data to a table. I am having difficulty with mapping because of the auditline array.
Does anyone know of way of flattening the auditline so that I can map my data correctly.
payload:
action:
id: 301
module : 2
name: "Create folder"
auditline:
0: Id: 1723
description:"Folder has been changed from Matlab to C#"
1: Id: 1724
description:"Folder name is Matlab"
This is the map function snippet.
<TableBody>
{data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map(n => {
const isSelected = this.isSelected(n.id);
return (
<TableRow
hover
onClick={event => this.handleClick(event, n.id)}
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={n.id}
selected={isSelected}
>
<TableCell>{n.action.name}</TableCell>
<TableCell>{n.auditLines.description}</TableCell>
<TableCell>
</TableCell>
</TableRow>
);
})}
You could map your array of audits to create multiple table cells :
<TableRow
hover
onClick={event => this.handleClick(event, n.id)}
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={n.id}
selected={isSelected}
>
<TableCell>{n.action.name}</TableCell>
{n.auditLines.map(audit => <TableCell key={audit.id}>{audit.description}</TableCell>)}
<TableCell>
</TableCell>
</TableRow>
Or, if you want all of them in a single one, use join
I have made a table using Material UI where I have two buttons in the first column of every row. I wish to edit/delete rows on clicking these but Im stuck on logic. Is it even possible with my implementation ? If not then what's the preferred way of doing so?
render() {
var deleteIcon =
(<IconButton onClick={console.log("delete")}>
<DeleteIcon color="secondary" />
</IconButton>
);
const editIcon = (
<IconButton onClick={console.log("edited")}>
<EditIcon color="primary" />
</IconButton>
);
return(
<TableBody>
{this.state.serviceData.map(n => {
return (
<TableRow key={n.id}>
<TableCell style={styles.editor} component="th" scope="row">
{deleteIcon}
{editIcon}
</TableCell>
<TableCell>{n.domain}</TableCell>
<TableCell>{n.service_name}</TableCell>
</TableCell>
</TableRow>
)};
And my result is :
Building on #st4rl00rd's comment, I was able to tie the buttons using :
const editIcon = (
<IconButton onClick={this.editItem}>
<EditIcon color="primary" />
</IconButton>
);
and binding them in the constructor. I was able to get the selected row data by doing :
<TableBody>
{this.state.serviceData.map(n => {
return (
<TableRow key={n.id} onClick={this.getData.bind(this,n)}>
I have recreated your problem and solved the problem with my logic.
I passed the index of each element as a parameter to the handler functions.
Eg:
const editIcon = index => (
<IconButton onClick={() => this.editComponent(index)}>
<EditIcon color="primary" />
</IconButton>
);
DELETION
For deletion, pass the index as params to the handler function and delete the element at specified index using splice operator.
deleteComponent(index) {
const serviceData = this.state.serviceData.slice();
serviceData.splice(index, 1);
this.setState({ serviceData });
}
EDITING
I have used a state called index to keep track of the index the user is currently editing. Initially the index is -1
So whenever the user clicks edit button the editedIndex is updated.
editComponent(index) {
this.setState({ editedIndex: index });
}
I created two TextField Component which is shown at the specified cell (the cell where editbutton is clicked)
const editDomain = (
<TextField
id="domain"
label="Domain"
className={classes.textField}
value={this.state.editedDomain}
margin="normal"
onChange={this.handleChange('editedDomain')}
/>
);
So Whenever the rendering component Index is equal to editedIndex the editing Compomemts are shown at corresponding Tablecell
<TableCell>
{serviceData.indexOf(n) === editedIndex
? editDomain
: n.domain}
</TableCell>
I suppose you want to do this
I have done same using React-Table here is the link for my project repo you can consider this as an example:
https://github.com/AdnanShah/ReactJS-KretaHub-/blob/Thank_You_Init/src/app/routes/dashboard/routes/Default/rows.js