4

I have an issue when I send SimpleForm (edit) request with react-admin. The request includes more parameters than I have in the form's fields.

For example I have form:

<Edit {...props}>
    <SimpleForm>
        <TextInput source="title_new" />
        <TextInput source="age_new" />
    </SimpleForm>
</Edit>

It includes only 2 fields but when I click "save" the request includes more fields. I understood that those fields are coming from the GET_ONE request which fill the data from the DB.

GET_ONE:

{
title: 'title',
title_new: 'title-new',
age: 'age',
age_new: 'age-new',
}

The update request UPDATE:

{
title: 'title',
title_new: 'title-new',
age: 'age',
age_new: 'age-new',
}

I expect that the UPDATE will include only the forms fields (title_new and age_new) without the title and age fields that come from "record".

Those fields make me a lot of trouble on the API side and I want to avoid/exclude them from all the forms, basically I want to send only the form inputs with the SimpleForm inputs only.

Few solutions I have in mind: 1. "Altering the Form Values before Submitting" here 2. Manipulate the request in the restProvider

Both solutions are not good for me because I have many forms like that and the restProvider code will look bad. Also I don't want to "alter" any form I build.

PLEASE ADVICE.

llioor
  • 5,804
  • 4
  • 36
  • 44
  • By the way, I asked similar question on AOR previous version: https://stackoverflow.com/questions/48600487/how-to-exclude-fields-from-simpleform – llioor Mar 05 '19 at 19:47

2 Answers2

2

This is the way react-admin works. If you want the UPDATE dataProvider verb to post only the changes fields (and probably send a PATCH rather than a POST), you have to do it in the dataProvider.

I'm not sure that the provider will look bad after the change: all you have to do is to alter the UPDATE verb. By default, it looks like (for the simple rest provider):

            case UPDATE:
                url = `${apiUrl}/${resource}/${params.id}`;
                options.method = 'PUT';
                options.body = JSON.stringify(params.data);
                break;

You just need to update it like so:

            case UPDATE:
                url = `${apiUrl}/${resource}/${params.id}`;
                options.method = 'PATCH';
-               options.body = JSON.stringify(params.data);
+               options.body = JSON.stringify(diff(params.data, param.previousData));
                break;

Where diff can be written as:

const diff = (previous, current) => lodash.pickBy(current, (v, k) => previous[k] !== v);
vaheeds
  • 2,594
  • 4
  • 26
  • 36
François Zaninotto
  • 7,068
  • 2
  • 35
  • 56
  • This looks like the correct way to handle it for all-requests rather than to handle it like `addUploadCapabilities` – llioor Mar 09 '19 at 18:57
  • @François Zaninotto: You modify `params.data` so you should change the verb from `PUT` to `PATCH`. – vaheeds Apr 30 '21 at 08:40
0

To selectively send just altered fields, use a diff function like this:

// in diff.js
import { transform, isEqual, isObject } from 'lodash';

/**
 * Deep diff between two object, using lodash
 * @param  {Object} object Object compared
 * @param  {Object} base   Object to compare with
 * @return {Object}        Return a new object who represent the diff
 */
const diff = (object, base) => {
  return transform(object, (result, value, key) => {
    if (!isEqual(value, base[key])) {
      result[key] =
        isObject(value) && isObject(base[key]) ? diff(value, base[key]) : value;
    }
  });
};

export default diff;

then:

// in dataProvider.js
update: (resource, params) =>
    httpClient(`${apiUrl}/${resource}/${params.id}`, {
      method: 'PATCH',
      body: JSON.stringify(diff(params.data, params.previousData)),
    }).then(({ json }) => ({ data: json })),
vaheeds
  • 2,594
  • 4
  • 26
  • 36