Add element
We have previously created a table of element, but what if we want to display add a new element?
We first need to create a page, here add-article.tsx. It will call our new PageContainer AddArticle.tsx.
Creating the form
This PageContainer will then call our form AddArticleForm.tsx , store in Form folder. You can use a regular form or create a multi-step form.
In this example we will show you how to set a multi-step form.
Here AddArticleForm.tsx will wrappe all form step store in a Steps folder while handling action form related. So there are 2 sections :
- Displaying the current step form
- Handle button form
Like for the search form we need a form reference. And also a current state to know what step form,we need to render.Optionally, we could add a stepper indicator.
import { WrapperForm, StepsPanel, WrapperStepContent } from "@components";
import { Form, Button, Space } from "antd";
import { FC, useState } from "react";
const [current, setCurrent] = useState(0);
const [form] = Form.useForm();
const steps = [
{
title: `${t("common:step")} 1`,
key: 0,
},
{
title: `${t("common:step")} 2`,
key: 1,
},
{
title: `${t("common:step")} 3`,
key: 2,
},
];
return (
<WrapperForm>
<StepsPanel currentStep={current} steps={steps} />
</WrapperForm>
);
Displaying steps
return (
<WrapperStepContent>
<Form form={form} scrollToFirstError>
{current === 0 && <AddArticleStep1 />}
{current === 1 && <AddArticleStep2 />}
{current === 2 && <AddArticleStep3 />}
</Form>
</WrapperStepContent>
);
We use Ant Design Form component and pass down our form reference. Then it will respectfully render a specific step regarding the current state.
A step form like AddArticleStep1 will only be composed by the form's item (input, checkbox , etc...)
return (
<>
<Form.Item
label={name}
name="name"
rules={[{ required: true, message: errorMessageEmptyInput }]}
>
<Input />
</Form.Item>
<Form.Item label={additionalDescription} name="additionalDescription">
<Input.TextArea />
</Form.Item>
</>
);
Handle actions
The second section handles all actions, like to navigate between steps or submitting the form.
caution
You want to display actions logically.
The first step doesn't need a back button. For the last step, you need a submit button not a next button
return (
<WrapperForm>
<StepsPanel {...props} />
<WrapperStepContent>{content}</WrapperStepContent>
{/* ACTIONS */}
{current === 0 ? (
<div style={{ textAlign: "center" }}>
<Button onClick={handleClickNext}>{t("actions:next-step")}</Button>
</div>
) : current > 0 && current < steps.length - 1 ? (
<div style={{ textAlign: "center" }}>
<Space>
<Button onClick={handleClickBack}>{t("actions:back-step")}</Button>
<Button onClick={handleClickNext}>{t("actions:next-step")}</Button>
</Space>
</div>
) : (
<div style={{ textAlign: "center" }}>
<Space>
<Button onClick={handleClickBack}>{t("actions:back-step")}</Button>
<Button type="primary" loading={createLoading} onClick={onFinish}>
{t("actions:submit")}
</Button>
</Space>
</div>
)}
{/* ACTIONS */}
</WrapperForm>
);
Each type of buttons needs specific props and function to trigger.
Go back
const handleClickBack = () => {
setCurrent(current - 1);
};
Go next
If all inputs are not valid, it will not go to the next step.
const handleClickNext = () => {
form
.validateFields()
.then(() => {
// Here make api call of something else
setCurrent(current + 1);
})
.catch((err) => console.log(err));
};
Submit
const onFinish = () => {
form
.validateFields()
.then(() => {
createArticle({ input: { ...form.getFieldsValue(true), accountId: 1 } });
})
.catch((err) => showError(t("messages:error-creating-data")));
};
Creating new element
For creating the new element we just call our mutation on button submit click.
const {
mutate,
isLoading: createLoading,
data,
} = useCreateArticleMutation<Error>(graphqlRequestClient, {
onSuccess: (
data: CreateArticleMutation,
_variables: CreateArticleMutationVariables,
_context: unknown
) => {
router.push(`/article/${data.createArticle.id}`);
showSuccess(t("messages:success-created"));
},
onError: (error) => {
showError(t("messages:error-creating-data"));
},
});
const createArticle = ({ input }: CreateArticleMutationVariables) => {
mutate({ input });
};
On success, we need to redirect the use of the new created element. We used NextJS Router to do so.
router.push(`/article/${data.createArticle.id}`);
Add button to access the page
In our PageContainer, here Articles.tsx, we will add a new action button to actionsRight property in HeaderContent component.
import { LinkButton } from "@components";
<HeaderContent
title={t("common:articles")}
routes={articlesSubRoutes}
actionsRight={
<Space>
<Button icon={<SearchOutlined />} onClick={() => openSearchDrawer()} />
<LinkButton
title={t("actions:add2", { name: t("common:article") })}
path="/add-article"
type="primary"
/>
</Space>
}
/>;