As of now when I was clicking on the edit icon
, a form
was getting opened and we used to change the data and save
it.
but now I have a requirement in which once user click
on edit icon the same row
,other than certificateNo
should be editable
. and edit
button should be replaced by save
. and user can save data there,without going to any other page.
How to approach that? any suggestions?
<tbody>
{response.map((certificate: Certificate,index:number) => {
return (
<tr key={certificate.certificateNo}>
<td>{certificate.certificateNo}</td>
<td>{certificate.sponser}</td>
<td>{certificate.protoColNo}</td>
<td>{certificate.startDate}</td>
<td>{certificate.endDate}</td>
<td>{certificate.noOfSubjects}</td>
<td>{certificate.country}</td>
<td>{certificate.requestStatus}</td>
<td>
<div className="btn-group mr-2">
<button className="btn btn-sm btn-outline-secondary" onClick={()=>updateCertificate(certificate,index)}>
<i className="bi bi-pencil"></i>
</button>
</div>
CodePudding user response:
You need some way of being able to identify the row being edited, and a place to save the current edited data. That can be achieved by using state.
When the id of an object in your data array matches the current selected row (after you've clicked the edit button) return one set of row data that includes input
elements. If the id doesn't match, return normal row data.
Here's a minimal working example.
const { useState } = React;
const data = [
{ id: 1, name: 'Rita', age: 22 },
{ id: 2, name: 'Sue', age: 42 },
{ id: 3, name: 'Bob', age: 12 }
]
function Example({ data }) {
// Have states for the data, the current selected row
// and the data that is currently being edited
const [ rowData, setRowData ] = useState(data);
const [ selectedRow, setSelectedRow ] = useState(0);
const [ editData, setEditData ] = useState({});
// Compiles the row data
function getRows() {
return rowData.map((obj, i) => {
// Get the id, name, and age from the
// current object
const { id, name, age } = obj;
// If the id of the current object equals
// the selected row return a set of row data with input
// elements (not id though) auto-populated with the
// information from that object
if (id === selectedRow) {
return (
<tr>
<td>{editData.id}</td>
<td>
<input
className="edit"
type="text"
name="name"
value={editData.name}/>
</td>
<td>
<input
className="edit"
type="text"
name="age"
value={editData.age}
/>
</td>
<button
data-id={id}
data-action="Save"
>Save
</button>
</tr>
);
}
// Otherwise return a set of non-editable
// row data
return (
<tr>
<td>{id}</td>
<td>{name}</td>
<td>{age}</td>
<button
data-id={id}
data-action="Edit"
>Edit
</button>
</tr>
);
});
}
// When a button has been clicked
// Note: because we're using event delegation
// (attaching listeners to the table element instead
// of all the rows) we need to check that element
// we clicked on is the button
function handleClick(e) {
const { nodeName } = e.target;
if (nodeName === 'BUTTON') {
const { id, action } = e.target.dataset;
// If the button action is edit, set the
// selected row state, and auto-populate the
// editData state
if (action === 'Edit') {
setSelectedRow( id);
const obj = data.find(obj => obj.id === id);
setEditData({...obj});
}
// If the action is save filter out the matching
// object from the data state, and then update the data
// with the filtered object, plus the object in
// the editData state, making sure you sort by id
// otherwise the edited row will appear at the bottom
// Then reset the states
if (action === 'Save') {
const filtered = data.filter(obj => obj.id !== selectedRow);
const updated = [...filtered, editData];
updated.sort((a, b) => a.id - b.id);
setRowData(updated);
setSelectedRow(0);
setEditData({});
}
}
}
// When an input changes
// Again we need to check that the element
// that has changed is an input
function handleChange(e) {
const { nodeName } = e.target;
// Update the editData state using the input
// name and value
if (nodeName === 'INPUT') {
const { name, value } = e.target;
setEditData({ ...editData, [name]: value });
}
}
// We attach two listeners to the table element
// One to catch clicks from the button
// The other to catch changes to the inputs if
// we're in edit mode
return (
<table
onClick={handleClick}
onChange={handleChange}
>
<tr class="heading">
<td>ID</td>
<td>Name</td>
<td>Age</td>
</tr>
<tbody>
{getRows()}
</tbody>
</table>
);
};
ReactDOM.render(
<Example data={data} />,
document.getElementById('react')
);
table { border: 1px solid #898989; border-collapse: collapse; }
td, input { width: 75px; text-align: center; }
td { padding: 0.2em; border: 1px solid #898989; }
.heading { background-color: #efefef; text-align: center; font-weight: 600; }
.edit { background-color: #ffffcc; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>