Closed DewangS closed 3 years ago
So, no one ran in to this issue? I really need some help to decide if I go ahead with bulk update or stay with just cell\row update .. it would be painful for the end user to perform 2 additional clicks per edit..
You can most def do this but you have to add the logic yourself. We expose the needed props/etc.. to allow consumers to make this table highly customizable. If you could provide a sandbox that would be great.
There isn't anything special in my code but here is a cutdown version of the code at code sandbox
steps to re-produce ....
The changes are not lost, but just hidden. And I just opened a PR to fix this.
@Domino987 That's really odd. I am on v.2.3.26 and can definitely see that changes are not persisted. So, here is what I've observed,
I use Formik for the Form management and my MaterialTable is inside my Formik form. I have two buttons, one to cancel changes and return the user back to the home page and the second button to SUBMIT the Formik form. after updating a few values, when I click the SAVE command button which in turn makes an API call to SAVE the updates, I observe no changes to the updated cells..below are relevant code snippets ...
//initial declaration of JSON array to store and render data fetched using the API call from useEffect() method
const [applicationList, setApplicationList] = React.useState([]);
//the useEffect where I fetch data via API call
useEffect(() => {
API.get("/api/ApplicationListByStatuses/27", {
//signal: signal,
})
.then(function (response) {
setApplicationList(response.data);
})
.catch(function (error) {
console.log(error);
});
}, []);
//below is form submit method where I update data using an API call. this is where the check on permit_number > 0 is always false.. see the if condition. I only need to submit if the value has been updated.
const submitForm = () => {
let updatedApplicationRecords = []
applicationList.forEach(application => {
if (application.permit_number>0) {
updatedApplicationRecords.push({
ApplicationID: application.application_id,
staffID: application.staff_id,
accessNumber: application.access_number,
appliactionStatusID: 2
})
}
});
console.log('+++ total records : ', updatedApplicationRecords)
API.post("/api/Application/updateApplications", updatedApplicationRecords, {
signal: signal,
})
.then(function (response) {
console.log('+++ RESPONSE ', response)
let status = response.status
if (response.status===200) {
console.log('+++ status', status)
} else {
console.log('Error occurred");
}
})
.catch(function (error) {
console.log("*** ERROR RESPONSE : ", error);
});
setTimeout(() => {
}, 500);
}
// below is my MT definition ..
<MaterialTable
title="Update Applications"
icons={tableIcons}
columns={columns}
data={applicationList}
options={{
showTitle: true,
isLoading: true,
pageSize: 10,
headerStyle: {
backgroundColor: "#01579b",
color: "#FFF",
}
}}
editable={{
onBulkUpdate: (changes) =>
new Promise((resolve, reject) => {
setTimeout(() => {
const dataUpdate = [...applicationList];
for (var key in changes) {
if (changes.hasOwnProperty(key)) {
dataUpdate[key] = changes[key].newData
}
}
setApplicationList([...dataUpdate]);
resolve();
}, 1000);
})
}}
/>
Unless I am doing something wrong in the MT onBulkUpdate method..
Hi, First of all, the PR I added should work for you anyway. But for your code, can you put that in a sandbox so I investigate it running, maybe with dummy data? Why would you put it in formik, if the table is tracking the data for you in the first place? Good you brought it up. I think this might be an error and i would like to get it fixed.
I will try to add this to the original sandbox code. My sincere apology, I actually realised that I don't use Formik in this functional component. i.e. the below SAVE button actually triggers the earlier mentioned submitForm method. While there would you please verify my onBulkUpdate() and the conditional logic ..the below 2 lines.... hope I am doing it right here. Also, just curious, what even gets fired in response to the accept and cancel changes button clicks .. these 2 buttons appears as soon as user clicks on the Bulk Update 'pen' icon .. one with correct icon and the other with a cross icon on the top right corner...
applicationList.forEach(application => { if (application.permit_number>0) {
<Button
variant="contained"
size="medium"
className={classes.buttonGreen}
startIcon={<SaveIcon />}
onClick={submitForm}
>
@DewangS I apologize, I misunderstood the issue at first but the sandbox cleared it up. Sorry I was not much help but @Domino987 seems to have this one under control.
@DewangS to not seeing them. I only opened a PR, so the code is neither merged nor releases. They will be with the next release though. Add the link to the new sandbox once you added the code, hit me up and i will check it out
here is the updated code, you will see alert with zero every time if you follow my previous steps on how to reproduce... just update a few License Number values codeSandbox
@oze4 No worries. I can understand that you guys are assisting us as much as you can on top of having your regular work. Much appreciated.
here is the updated code, you will see alert with zero every time if you follow my previous steps on how to reproduce... just update a few License Number values codeSandbox
Yes as mentioned, the values are not updated in the table itself during edit, but saving will still update the previously entered values. And the new PR, which just got merged will show the correct values on pagination. You still need to save to trigger the onBulkUpdate at the end. Add you do not need the ´setTimeout´ to artificial create a loading spinner btw.
sorry but how do you manually trigger the onBulkUpdate? is there any way? i.e. say user updates one or more values and directly hit the save button which is a natural reaction on any web form, is there a way to accept the changes by manually triggering the onBulkUpdate?
Yes. You need to use the tableRef for that. Here is an example:
import React, { useState } from "react";
import { Button, Box, Grid } from "@material-ui/core";
import MaterialTable from "@material-table/core";
import SaveIcon from "@material-ui/icons/Save";
import CancelIcon from "@material-ui/icons/Cancel";
import { tableIcons } from "./material-table-icons";
import { green, red } from "@material-ui/core/colors";
const MTbulkUpdateTest = () => {
const ref = React.useRef(null)
const columns = [
{
field: "lastName",
title: "Surname",
cellStyle: {
width: 150,
minWidth: 150,
whiteSpace: "nowrap"
},
headerStyle: {
width: 150,
minWidth: 150,
whiteSpace: "nowrap"
}
},
{
field: "firstName",
title: "Other Name",
cellStyle: {
width: 150,
minWidth: 150,
whiteSpace: "nowrap"
},
headerStyle: {
width: 150,
minWidth: 150,
whiteSpace: "nowrap"
}
},
{
field: "licenseNo",
title: "License Number",
cellStyle: {
width: 150,
minWidth: 150,
whiteSpace: "nowrap"
},
headerStyle: {
width: 150,
minWidth: 150,
whiteSpace: "nowrap"
}
}
];
const [applicationList, setApplicationList] = useState([
{ firstName: "Roger", lastName: "Grisham", licenseNo: 0 },
{ firstName: "Tracey", lastName: "Duckworth", licenseNo: 0 },
{ firstName: "Karina", lastName: "Fred", licenseNo: 0 },
{ firstName: "Bob", lastName: "Dillon", licenseNo: 0 },
{ firstName: "Frank", lastName: "Sinatra", licenseNo: 0 },
{ firstName: "Roger", lastName: "Grisham", licenseNo: 0 },
{ firstName: "Tracey", lastName: "Duckworth", licenseNo: 0 },
{ firstName: "Karina", lastName: "Fred", licenseNo: 0 },
{ firstName: "Bob", lastName: "Dillon", licenseNo: 0 },
{ firstName: "Frank", lastName: "Sinatra", licenseNo: 0 },
{ firstName: "George", lastName: "Song", licenseNo: 0 },
{ firstName: "Henry", lastName: "Luise", licenseNo: 0 },
{ firstName: "Lora", lastName: "Turnbull", licenseNo: 0 },
{ firstName: "Vicrtor", lastName: "Franko", licenseNo: 0 },
{ firstName: "Ashley", lastName: "Martin", licenseNo: 0 },
{ firstName: "Greg", lastName: "Hunt", licenseNo: 0 },
{ firstName: "Cathy", lastName: "Noel", licenseNo: 0 },
{ firstName: "Vicki", lastName: "Matrin", licenseNo: 0 },
{ firstName: "Jag", lastName: "Rogerson", licenseNo: 0 },
{ firstName: "Kim", lastName: "Kard", licenseNo: 0 }
]);
const submitForm = () => {
let counter = 0;
Object.values(ref.current.dataManager.bulkEditChangedRows).forEach(({newData}) => {
if (newData.licenseNo > 0) {
counter++;
}
});
alert("+++ Total Updated Records : " + counter);
};
return (
<div>
<Grid container direction="row" justify="center" alignItems="center">
<Grid
container
item
lg={12}
md={12}
xs={12}
m={5}
spacing={5}
justify="space-between"
style={{ marginTop: "2em" }}
>
<Box textAlign="center" m={2}>
<MaterialTable
title="Bulk Update Test"
icons={tableIcons}
columns={columns}
tableRef={ref}
data={applicationList}
onChangePage={(event, rowData) => {
console.log("++ changed page", event);
}}
options={{
showTitle: true,
isLoading: true,
pageSize: 10,
headerStyle: {
backgroundColor: "#01579b",
color: "#FFF"
}
}}
editable={{
onBulkUpdate: (changes) =>
new Promise((resolve, reject) => {
setTimeout(() => {
const dataUpdate = [...applicationList];
for (var key in changes) {
if (changes.hasOwnProperty(key)) {
dataUpdate[key] = changes[key].newData;
}
}
setApplicationList([...dataUpdate]);
resolve();
}, 1000);
}),
onRowUpdate: (newData, oldData) =>
new Promise((resolve, reject) => {
setTimeout(() => {
const dataUpdate = [...applicationList];
const index = oldData.tableData.id;
dataUpdate[index] = newData;
setApplicationList([...dataUpdate]);
resolve();
}, 1000);
})
}}
/>
</Box>
</Grid>
</Grid>
<Box textAlign="right" mx={15} my={3}>
<Button
variant="contained"
color="secondary"
size="medium"
startIcon={<CancelIcon />}
//disabled={isSubmitting}
onClick={() => {
//setConfirmOpen(true);
}}
>
Cancel
</Button>
<Button
variant="contained"
color="primary"
size="medium"
startIcon={<SaveIcon />}
onClick={submitForm}
>
Save
</Button>
</Box>
</div>
);
};
export default MTbulkUpdateTest;
Perfect! Thanks @Domino987 I just tried this in my sandbox and it worked. Much appreciated.
ooops.. Houston we have a problem. I was bit to excited to report the success. Just tried other way round i.e. updated a few values though this time around 'ACCEPTED' my changes on each page then hit the 'SAVE' button and and now getting 0 rows updated. I guess I have check both... see below... i.e. the Ref and the applicationList in my submitForm(). Am I right?
const submitForm = () => {
let counter = 0;
Object.values(ref.current.dataManager.bulkEditChangedRows).forEach(
({ newData }) => {
if (newData.licenseNo > 0) {
counter++;
}
}
);
applicationList.forEach(
(application) => {
if (application.licenseNo > 0) {
counter++;
}
}
);
alert("+++ Total Updated Records : " + counter);
};
Depends on what you need to do, Changes/bulkEditChangedRows contains only changed fields. SO if you need all above 0, you would need to check both yes.
Guidelines
Please include a demo of the issue/behavior/question you have
Please try to be as detailed as possible
I have a functional component which uses MT's bulk update feature. Everything works as expected so, no issues there though... What I noticed is, after clicking the 'Edit' (Pencil Icon) button on the top right corner, all editable elements get enabled for the user input and user can update values in those elements. The issue is, user MUST click the tick (correct icon) in order to ACCEPT those changes, if user miss to ACCEPT those changes and move to the next\previous page or say click a command button on the page, user will end up losing their changes.
Is there any way to force user to ACCEPT or REJECT their changes when they perform activities like this? i.e. clicking any other button etc? I have a SAVE and CANCEL command buttons to save changes to the database via API, Ideally I would like to ACCEPT all outstanding changes on the page before running button's click handler.
You may fork one of the following starter templates if you would like:
CodeSandbox
StackBlitz