Skip to main content

4.2. Adding Edit Page

This post demonstrates how to implement the edit page of blog_posts resource without resorting to <MuiEditInferencer />. Basically, we just replace the existing code inside the src/pages/blog-posts/edit.tsx file with the Inferencer-generated refine and Material UI code.

The Edit Page

The Inferencer-generated <BlogPostEdit /> component is available at the /blog-posts/edit/:id route. For a blog post with a given :id, when we visit its edit page, we can view the code in a modal by clicking on Show the auto-generated code button:

Show generated code

import { Edit, useAutocomplete } from "@refinedev/mui";
import { Box, TextField, Autocomplete } from "@mui/material";
import { useForm } from "@refinedev/react-hook-form";
import { Controller } from "react-hook-form";

export const BlogPostEdit = () => {
const {
saveButtonProps,
refineCore: { queryResult },
register,
control,
formState: { errors },
} = useForm();

const blogPostsData = queryResult?.data?.data;

const { autocompleteProps: categoryAutocompleteProps } = useAutocomplete({
resource: "categories",
defaultValue: blogPostsData?.category?.id,
});

return (
<Edit saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
{...register("id", {
required: "This field is required",
valueAsNumber: true,
})}
error={!!(errors as any)?.id}
helperText={(errors as any)?.id?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="number"
label="Id"
name="id"
disabled
/>
<TextField
{...register("title", {
required: "This field is required",
})}
error={!!(errors as any)?.title}
helperText={(errors as any)?.title?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="text"
label="Title"
name="title"
/>
<TextField
{...register("content", {
required: "This field is required",
})}
error={!!(errors as any)?.content}
helperText={(errors as any)?.content?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
multiline
label="Content"
name="content"
/>
<Controller
control={control}
name="category"
rules={{ required: "This field is required" }}
// eslint-disable-next-line
defaultValue={null as any}
render={({ field }) => (
<Autocomplete
{...categoryAutocompleteProps}
{...field}
onChange={(_, value) => {
field.onChange(value);
}}
getOptionLabel={(item) => {
return (
categoryAutocompleteProps?.options?.find(
(p) =>
p?.id?.toString() ===
item?.id?.toString(),
)?.title ?? ""
);
}}
isOptionEqualToValue={(option, value) =>
value === undefined ||
option?.id?.toString() === value?.id?.toString()
}
renderInput={(params) => (
<TextField
{...params}
label="Category"
margin="normal"
variant="outlined"
error={!!(errors as any)?.category?.id}
helperText={
(errors as any)?.category?.id?.message
}
required
/>
)}
/>
)}
/>
<TextField
{...register("status", {
required: "This field is required",
})}
error={!!(errors as any)?.status}
helperText={(errors as any)?.status?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="text"
label="Status"
name="status"
/>
{/*
DatePicker component is not included in "@refinedev/mui" package.
To use a <DatePicker> component, you can follow the official documentation for Material UI.

Docs: https://mui.com/x/react-date-pickers/date-picker/#basic-usage
*/}
<TextField
{...register("createdAt", {
required: "This field is required",
})}
error={!!(errors as any)?.createdAt}
helperText={(errors as any)?.createdAt?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
label="Created At"
name="createdAt"
/>
</Box>
</Edit>
);
};

The code above was generated by Inferencer by carrying out an initial polling to the /blog-posts/edit/:id endpoint in Simple REST API. After polling, it figured out the shape of the API response and placed appropriate Material UI form elements for editing a blog post item.

Below, we briefly make sense of the components and hooks used in the edit page.

Understanding the Edit Page Components

Refer to the Material UI documentation for more information

Let's copy this code generated by Inferencer and paste it into the file at src/pages/blog-posts/edit.tsx. We should replace the existing code that uses the <MuiEditInferencer />.

Handling Relationships

The blog_posts resource is associated with categories, so for editing the category of a blog post, we need to fetch it and pass it to its form field. In order to avail a blog post's associated categories data, we are using the useAutocomplete() hook before passing them to the <Autocomplete /> component:

const { autocompleteProps: categoryAutocompleteProps } = useAutocomplete({
resource: "categories",
defaultValue: blogPostsData?.category?.id,
});

refine's Material UI useAutoComplete() Hook

The useAutoComplete() hook is a refine supported Material UI hook that fetches data by passing its accepted arguments to the dataProvider's getList method. Then, it returns the necessary props for the <Autocomplete /> component.

By default, useAutocomplete() returns 10 items. This has drawbacks because the category of a current blog post may not be in the first 10 records. So, in the above useAutcomplete() implementation, we are using the defaultValue prop to overcome this behavior. By passing the category :id of the current blog post as the defaultValue, we are making sure we always fill the category field for editing a given blog post item.

Refer to the useAutocomplete() documentation for more information

Refer to the Material UI <Autocomplete /> documentation for more information

Now, if we view the blog posts edit page in the browser at localhost:5173/blog-posts/edit/123, it shows the same UI:

Edit Page Preview

localhost:5173/blog-posts/edit/123

Notice also that we no longer see the box that houses Show the auto-generated code button, because we removed the <MuiEditInferencer /> component from <BlogPostEdit />:



Checklist