react-admin beforeGetOne add url-query-parameters

41 views Asked by At

I have a react-admin app. Here I have an <Edit>-component:

export const UserEdit = (props) => {
  const value = useContext(AppContext);

  return (
    <div className="container-lg px-0 pb-1">
      <Edit queryOptions={{ meta: { customerId: value.selectedCustomer.id } }} mutationOptions={{ meta: { customerId: value.selectedCustomer.id } }}>
        <SimpleForm /*toolbar={<EditToolbar />}*/>

          <Stack direction="row" spacing={2}>
            <Stack direction="column">
              <TextInput source="id" required disabled />
              <TextInput source="first_name" required />
              <TextInput source="last_name" required />
              <TextInput source="email" type="email" required disabled />
              <PreferredLanguageField {...props} />
            </Stack>

            <Stack direction="column">
              <FormDataConsumer>
                {({ formData, ...rest }) => (
                  <>
                    <BooleanInput source="active_customer_membership.user_access_manager" label="Can manage users" disabled={value.user.id == formData.id} />
                    <UserRoleInput {...props} disabled={value.user.id == formData.id} />
                    <UserAccessInput {...props} formData={formData} disabled={value.user.id == formData.id} />
                  </>
                )}
              </FormDataConsumer>
            </Stack>
          </Stack>

        </SimpleForm>
      </Edit>
    </div>
  );
};

In Main I have a dataProvider:

const baseDataProvider = restProvider('../../api/user_access_management/v01', fetchUtils.fetchJson);
export const dataProvider = withLifecycleCallbacks(baseDataProvider, [
  {
      resource: 'users',

      beforeGetList: async (params, dataProvider) => {
        params.filter.customer_id = params.meta.customerId;
        return params
      },

      afterGetList: async (result, dataProvider) => {
        // Format the retrieved data to the correct format for react-admin.
        return {
          data:  result.data.users,
          total: result.total
        };
      },

      beforeGetOne: async (params, dataProvider) => {
        return params
      },

      afterGetOne: async (result, dataProvider) => {
        return {
          data: result.data.user
        };
      },

      beforeUpdate: async (params, dataProvider) => {
        params.data = {
          user: DataHelper.formUser(params.data, params.meta.customerId)
        };

        return params;
      },

      afterUpdate: async (result, dataProvider) => {
        return {
          data: result.data.user
        };
      },

      beforeCreate: async (params, dataProvider) => {
        params.data = {
          user: DataHelper.formUser(params.data, params.meta.customerId),
        };

        return params;
      },

      afterCreate: async (result, dataProvider) => {
        return {
          data: result.data.user
        };
      },
  },
]);

And how the App is initialized:

const Main = (props) => {

  const [customer, setCustomer] = useState(props.props.customer);
  useEffect(() => {
    setCustomer(props.props.customer);
  }, [props.props.customer]);

  const [customers, setCustomers] = useState(props.props.customers);
  useEffect(() => {
    setCustomers(props.props.customers);
  }, [props.props.customers]);

  const [user, setUser] = useState(props.props.user);
  useEffect(() => {
    setUser(props.props.user);
  }, [props.props.user]);

  const [selectedCustomer, setSelectedCustomer] = useState(props.props.customers[0]);
  useEffect(() => {
    setSelectedCustomer(props.props.customers[0]);
  }, [props.props.selectedCustomer]);

  return (
    <AppContext.Provider value={{ customer: customer, customers: customers, user: user, selectedCustomer: selectedCustomer, setSelectedCustomer: setSelectedCustomer }}>
      <Admin dataProvider={dataProvider} layout={CustomLayout}>
        <Resource name="users"
                  list={<UserList />}
                  edit={<UserEdit />}
                  create={<UserCreate />}
        />
      </Admin>
    </AppContext.Provider>
  );
}

export default Main;

My problem now is that for the Create / Update, I can add customer_id as a field to send to the backend (they're POST / PUT requests). Same for GetList, here I can add customer_id as filter: params.filter.customer_id = params.meta.customerId;

However, I also need the customer_id in the backend for the GetOne-request. The <Edit> component has queryOptions={{ meta: { customerId: value.selectedCustomer.id } }}, and I see that in the beforeGetOne -callback. However, adding it to filters (like I do for GetList) doesn't seem to work.

How would I add query-parameters to the URL for the GetOne-request?

1

There are 1 answers

0
slax57 On

restProvider, which I assume you get from ra-data-simple-rest, doesn't support passing filters to getOne, as it is non-standard (in most implementations, the id is all you need to get a single record).

You will probably need to build the HTTP request manually (using fetch).

Alternatively, you can pass the customerId as a custom HTTP Header but then your API needs to know how to use it.

Lastly, you can also choose to override the getOne implementation of your dataProvider, to have it call getList instead, with the filter, and return only the first result.

Regarding the call to delete, you need to pass a custom toolbar to your <SimpleForm>, and in it include a custom <DeleteButton> on which you can pass mutationOptions.

PS: in case you are using the customerId as a way to build a multi-tenant app, be sure to read Multi-Tenant Single-Page Apps: Dos and Don'ts to avoid the most common mistakes.