Update related objects

Hi there, I’m looking for a way to update nested objects using Refine. Did not yet settle on a data provider but initially thought about using Hasura (GraphQL) on top of postgres. It allowed me to easily query multiple nested related objects and using the Ant Design system I worked out to load the nested objects in an edit form using <Form.List>. Usecase I'd like to make a page to create and edit invoices, in which the underlying tasks can be edited within the same form. Using the AntD I was able to create a working frontend (see attachment), but can't yet handle the mutation / creation in the database. The frontend allows me to remove and add tasks, which returns the correct values. Below you can see the console.log(values) caught in the onFinish Task 1 and Task 2 were loaded from the db. Task 3 was added in the frontend: { customer_id: 1, invoice_text: "Invoice text example", tasks: [ { name: "Task 1 name", description: "Description of task 1" }, { name: "Task 2 name", description: "Description of task 2" }, { name: "Task 3 name", description: "Description of task 3 etc. etc." } ] } However, it turns out Hasura does not yet allow nested mutations since it’s still on their roadmap: https://github.com/hasura/graphql-engine/issues/1573 I was therefor wondering whether another Data Provider would be able to handle these type of mutations? And if not, would it be possible to handle the mutations/creations of the tasks tasks separately? Any pointers on this? Thanks in advance! Thomas
GitHub
update nested object · Issue #1573 · hasura/graphql-engine
First, thank you very much for your work on Hasura, its a great product. There is a feature that I would be very happy to have, wanted to know what are your thoughts on it. Currently there Is a nes...
No description
8 Replies
eager-peach
eager-peach2y ago
Hİ @prikkel., i will investigate to issue and return you as soon as possible
optimistic-gold
optimistic-gold2y ago
Thanks @alicanerdurmaz !
Omer
Omer2y ago
Hi @prikkel. , You can directly use data hooks like useCreate/useUpdate/useDelete, etc for mutation
Omer
Omer2y ago
optimistic-gold
optimistic-gold2y ago
Hi Omar, Thanks for the quick reply. Unfortunately the link you sent doesn’t seem to contain any reference to useCreate or useUpdate, since everything is handled by the useForm component. Your remark did intrigue me so I went looking for ways to manually input the tasks. After running into some problems with calling hooks in the wrong places I succeeded in added tasks by hand by declaring the useCreate outside the components: const { mutate } = useCreate<ITaken>(); And calling mutate inside onFinish as shown here https://refine.dev/docs/faq/#how-can-i-change-the-form-data-before-submitting-it-to-the-api : mutate({ resource: "taken" , values: { offerte_id: 1, naam: "Task 3", omschrijving: "Description of Taak 3" }, } ); I’ll look into how to handle the different scenarios and get back if I have any further questions or worked out the solution a bit more!
FAQ | refine
How can I change the form data before submitting it to the API?
optimistic-gold
optimistic-gold2y ago
<Form {...formProps} layout="vertical" onFinish={(values) => { console.log(values); if ("taken" in values) { const { taken } = values; // check against initialValues: // if id missing -> useDelete // if id already existed -> useUpdate // if id empty -> useCreate // mutate( // { // resource: "taken", // values: { // offerte_id: 1, // naam: "Task 3", // omschrijving: "Description of Taak 3" // }, // } // ); delete values["taken"]; } return ( formProps.onFinish && formProps.onFinish({ ...values }) ); }}>
Omer
Omer2y ago
Did it work? 🙂
optimistic-gold
optimistic-gold2y ago
It did! To edit the tasks, I would remove the tasks from the submitted values and handle each according to whether or not it already existed or was removed alltogether:
const { mutate: mutateCreate } = useCreate<ITaken>();
const { mutate: mutateUpdate } = useUpdate<ITaken>();
const { mutate: mutateDelete } = useDelete<ITaken>();

const handleSubmit = (values : any) => {
// check against initialValues:
let initialTaken = formProps.initialValues?.taken;
if ("taken" in values) {
const { taken } = values;
console.log(taken);
if (Array.isArray(taken)) {
taken.forEach(function (taak, index) {
console.log(taak, index);
if (!("id" in taak)) {
console.log('Create task!')
// if id empty -> useCreate
mutateCreate( { resource: "taken", values: taak, } );
} else {
const { id } = taak;
console.log('Update Task');
// if id already existed -> useUpdate
mutateUpdate( { resource: "taken", id : id, values: {...taak, offerte_id: offerte_id}, } );
// remove task from list of initial tasks
initialTaken = initialTaken.filter((obj: any) => obj.id !== id);
}
});

// if id missing -> useDelete
initialTaken.forEach(function (taak: any, index: any) {
if ("id" in taak) {
console.log('Delete task');
const { id } = taak;
mutateDelete( { resource: "taken", id: id, } );
}
});
}
delete values["taken"];
}
return ( formProps.onFinish && formProps.onFinish({ ...values }) )
}
const { mutate: mutateCreate } = useCreate<ITaken>();
const { mutate: mutateUpdate } = useUpdate<ITaken>();
const { mutate: mutateDelete } = useDelete<ITaken>();

const handleSubmit = (values : any) => {
// check against initialValues:
let initialTaken = formProps.initialValues?.taken;
if ("taken" in values) {
const { taken } = values;
console.log(taken);
if (Array.isArray(taken)) {
taken.forEach(function (taak, index) {
console.log(taak, index);
if (!("id" in taak)) {
console.log('Create task!')
// if id empty -> useCreate
mutateCreate( { resource: "taken", values: taak, } );
} else {
const { id } = taak;
console.log('Update Task');
// if id already existed -> useUpdate
mutateUpdate( { resource: "taken", id : id, values: {...taak, offerte_id: offerte_id}, } );
// remove task from list of initial tasks
initialTaken = initialTaken.filter((obj: any) => obj.id !== id);
}
});

// if id missing -> useDelete
initialTaken.forEach(function (taak: any, index: any) {
if ("id" in taak) {
console.log('Delete task');
const { id } = taak;
mutateDelete( { resource: "taken", id: id, } );
}
});
}
delete values["taken"];
}
return ( formProps.onFinish && formProps.onFinish({ ...values }) )
}
In order to create I first needed to create the project, catch the generated ID and add the tasks afterwards. Still looking into how to handle any exceptions better but works for now.
const { mutate: mutateCreate } = useCreate<ITaken>();

const handleSubmit = async (values: any) => {
if ("taken" in values) {
const { taken } = values;

delete values["taken"];
// create project / invoice
const response = await onFinish({...values})
const offerte_id = response?.data?.id
// create tasks
if (Array.isArray(taken)) {
taken.forEach(function (taak, index) {
console.log('Create task!')
// if id empty -> useCreate
mutateCreate({
resource: "taken",
values: {...taak, offerte_id: offerte_id},
});
});
}
}
}
const { mutate: mutateCreate } = useCreate<ITaken>();

const handleSubmit = async (values: any) => {
if ("taken" in values) {
const { taken } = values;

delete values["taken"];
// create project / invoice
const response = await onFinish({...values})
const offerte_id = response?.data?.id
// create tasks
if (Array.isArray(taken)) {
taken.forEach(function (taak, index) {
console.log('Create task!')
// if id empty -> useCreate
mutateCreate({
resource: "taken",
values: {...taak, offerte_id: offerte_id},
});
});
}
}
}