rare-sapphire
rare-sapphire10mo ago

useSelect with useForm in antd

On edit page, I have list of roles, containing id as number and name as string, using useSelect with Select. But It shows the ID only and when submit to server, ID goes as string. Anyway to resolve that
14 Replies
kapa.ai
kapa.ai10mo ago
Hi @viveksingh0143. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Yes, you can resolve this issue by using the useSelect hook provided by Refine. This hook allows you to manage the <Select> component when records in a resource need to be used as select options. It also ensures that the defaultValue is included in the options. Here is an example from the Refine documentation:
import { useSelect } from "@refinedev/antd";
import { Select } from "antd";

interface IRole {
id: number;
name: string;
}

const RoleEdit: React.FC = () => {
const { selectProps } = useSelect<IRole>({
resource: "roles",
defaultValue: 11,
});

return (
<Select
placeholder="Select a role"
style={{ width: 300 }}
{...selectProps}
/>
);
};
import { useSelect } from "@refinedev/antd";
import { Select } from "antd";

interface IRole {
id: number;
name: string;
}

const RoleEdit: React.FC = () => {
const { selectProps } = useSelect<IRole>({
resource: "roles",
defaultValue: 11,
});

return (
<Select
placeholder="Select a role"
style={{ width: 300 }}
{...selectProps}
/>
);
};
In this example, useSelect is used to fetch the roles and the defaultValue is set to the id of the role that should be selected by default. The selectProps returned by useSelect are then spread into the <Select> component to populate it with options and preselect the default value. For the issue of ID going as string, you can handle it in the submit function by converting the string ID back to a number before sending it to the server. Source: Refine Documentation (https://refine.dev/docs/api-reference/antd/hooks/field/useSelect) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai10mo ago
<@940119979334967366> kapa.ai is still learning and improving, please let us know how it did by reacting below
rare-sapphire
rare-sapphire10mo ago
@kapa.ai I am using with combination of Form.Item, and it shows tags with blank values export interface IRole { id: number; name: string; status: "ACTIVE" | "INACTIVE"; abilities: any[] } const { formProps, saveButtonProps, queryResult } = useForm<IUser>(); const usersData = queryResult?.data?.data; const { selectProps: rolesSelectProps } = useSelect<IRole>({ resource: "roles", defaultValue: usersData?.roles?.map(r => r.id) }); <Form.Item label={translate("users.fields.roles")} name={["roles"]} rules={[{ required: true }]} > <Select mode="multiple" {...rolesSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" /> </Form.Item>
kapa.ai
kapa.ai10mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The issue you're facing might be due to the way you're setting the defaultValue in the useSelect hook. The defaultValue should be the id of the role that should be selected by default. However, in your case, you're setting it to usersData?.roles?.map(r => r.id), which is an array of role IDs. This might be causing the Select component to display tags with blank values. Here's how you can modify your code:
import { useSelect, useForm } from "@pankod/refine";
import { Select, Form } from "antd";

export interface IRole {
id: number;
name: string;
status: "ACTIVE" | "INACTIVE";
abilities: any[]
}

const RoleEdit: React.FC = () => {
const { formProps, saveButtonProps, queryResult } = useForm<IUser>();
const usersData = queryResult?.data?.data;

const { selectProps: rolesSelectProps } = useSelect<IRole>({
resource: "roles",
defaultValue: usersData?.roles[0]?.id, // Set the default value to the id of the first role
});

return (
<Form {...formProps}>
<Form.Item
label="Roles"
name={["roles"]}
rules={[{ required: true }]}
>
<Select mode="multiple" {...rolesSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" />
</Form.Item>
// Rest of your form items...
</Form>
);
};
import { useSelect, useForm } from "@pankod/refine";
import { Select, Form } from "antd";

export interface IRole {
id: number;
name: string;
status: "ACTIVE" | "INACTIVE";
abilities: any[]
}

const RoleEdit: React.FC = () => {
const { formProps, saveButtonProps, queryResult } = useForm<IUser>();
const usersData = queryResult?.data?.data;

const { selectProps: rolesSelectProps } = useSelect<IRole>({
resource: "roles",
defaultValue: usersData?.roles[0]?.id, // Set the default value to the id of the first role
});

return (
<Form {...formProps}>
<Form.Item
label="Roles"
name={["roles"]}
rules={[{ required: true }]}
>
<Select mode="multiple" {...rolesSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" />
</Form.Item>
// Rest of your form items...
</Form>
);
};
In this example, the defaultValue is set to the id of the first role in the usersData.roles array. This should preselect the corresponding role in the Select component and display its name instead of a blank tag. Please note that this is a simplified example and the actual implementation might vary based on your specific use case and the structure of your data. Source: Refine Documentation (https://refine.dev/docs/api-reference/antd/hooks/field/useSelect) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai10mo ago
<@940119979334967366> kapa.ai is still learning and improving, please let us know how it did by reacting below
rare-sapphire
rare-sapphire10mo ago
@kapa.ai But roles have multiple values and <Select> is referred to multiple mode
kapa.ai
kapa.ai10mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- In the case of multiple selections, you can indeed pass an array of IDs as the defaultValue to the useSelect hook. However, if you're seeing blank values, it might be due to the fact that the labels for these IDs are not fetched yet when the component renders. Refine's useSelect hook handles this by sending a request via the useMany hook to fetch the data and mark them as selected. This ensures that the labels for the default values exist in the current options array. Here's how you can modify your code:
import { useSelect, useForm } from "@pankod/refine";
import { Select, Form } from "antd";

export interface IRole {
id: number;
name: string;
status: "ACTIVE" | "INACTIVE";
abilities: any[]
}

const RoleEdit: React.FC = () => {
const { formProps, saveButtonProps, queryResult } = useForm<IUser>();
const usersData = queryResult?.data?.data;

const { selectProps: rolesSelectProps } = useSelect<IRole>({
resource: "roles",
defaultValue: usersData?.roles?.map(r => r.id), // Array of role IDs
});

return (
<Form {...formProps}>
<Form.Item
label="Roles"
name={["roles"]}
rules={[{ required: true }]}
>
<Select mode="multiple" {...rolesSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" />
</Form.Item>
// Rest of your form items...
</Form>
);
};
import { useSelect, useForm } from "@pankod/refine";
import { Select, Form } from "antd";

export interface IRole {
id: number;
name: string;
status: "ACTIVE" | "INACTIVE";
abilities: any[]
}

const RoleEdit: React.FC = () => {
const { formProps, saveButtonProps, queryResult } = useForm<IUser>();
const usersData = queryResult?.data?.data;

const { selectProps: rolesSelectProps } = useSelect<IRole>({
resource: "roles",
defaultValue: usersData?.roles?.map(r => r.id), // Array of role IDs
});

return (
<Form {...formProps}>
<Form.Item
label="Roles"
name={["roles"]}
rules={[{ required: true }]}
>
<Select mode="multiple" {...rolesSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" />
</Form.Item>
// Rest of your form items...
</Form>
);
};
In this example, the defaultValue is set to an array of role IDs. The useSelect hook sends a useMany query to the backend with these IDs and appends them to the options of the Select component. Please note that this is a simplified example and the actual implementation might vary based on your specific use case and the structure of your data. Source: Refine Documentation (https://refine.dev/docs/api-reference/antd/hooks/field/useSelect) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai10mo ago
<@940119979334967366> kapa.ai is still learning and improving, please let us know how it did by reacting below
rare-sapphire
rare-sapphire10mo ago
@kapa.ai yet blank tags, can't any way, that select match the string with number as ID
kapa.ai
kapa.ai10mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The issue you're facing might be due to the data type mismatch between the defaultValue and the value of the options in the Select component. If the defaultValue is a string and the value of the options is a number, Ant Design's Select component might not be able to match them correctly, resulting in blank tags. To resolve this, you can convert the defaultValue to a number before passing it to the useSelect hook. Here's how you can modify your code:
import {{ useSelect, useForm }} from "@pankod/refine";
import {{ Select, Form }} from "antd";

export interface IRole {{
id: number;
name: string;
status: "ACTIVE" | "INACTIVE";
abilities: any[]
}}

const RoleEdit: React.FC = () => {{
const {{ formProps, saveButtonProps, queryResult }} = useForm<IUser>();
const usersData = queryResult?.data?.data;

const {{ selectProps: rolesSelectProps }} = useSelect<IRole>({{
resource: "roles",
defaultValue: usersData?.roles?.map(r => Number(r.id)), // Convert string IDs to numbers
}});

return (
<Form {{...formProps}}>
<Form.Item
label="Roles"
name={{["roles"]}}
rules={{[{{ required: true }}]}}
>
<Select mode="multiple" {{...rolesSelectProps}} allowClear onSearch={{undefined}} filterOption={{true}} optionFilterProp="label" />
</Form.Item>
// Rest of your form items...
</Form>
);
}};
import {{ useSelect, useForm }} from "@pankod/refine";
import {{ Select, Form }} from "antd";

export interface IRole {{
id: number;
name: string;
status: "ACTIVE" | "INACTIVE";
abilities: any[]
}}

const RoleEdit: React.FC = () => {{
const {{ formProps, saveButtonProps, queryResult }} = useForm<IUser>();
const usersData = queryResult?.data?.data;

const {{ selectProps: rolesSelectProps }} = useSelect<IRole>({{
resource: "roles",
defaultValue: usersData?.roles?.map(r => Number(r.id)), // Convert string IDs to numbers
}});

return (
<Form {{...formProps}}>
<Form.Item
label="Roles"
name={{["roles"]}}
rules={{[{{ required: true }}]}}
>
<Select mode="multiple" {{...rolesSelectProps}} allowClear onSearch={{undefined}} filterOption={{true}} optionFilterProp="label" />
</Form.Item>
// Rest of your form items...
</Form>
);
}};
In this example, the defaultValue is set to an array of role IDs, which are converted to numbers using the Number() function. This should ensure that the defaultValue matches the value of the options in the Select component. Please note that this is a simplified example and the actual implementation might vary based on your specific use case and the structure of your data. Source: Refine Documentation (https://refine.dev/docs/api-reference/antd/hooks/field/useSelect) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai10mo ago
<@940119979334967366> kapa.ai is still learning and improving, please let us know how it did by reacting below
rare-sapphire
rare-sapphire10mo ago
@kapa.ai useSelect auto convert the optionValue to string, can we avoid in any way @kapa.ai Got the solution, I am mentioning for someone, who have stuck in similar situation: <Form.Item label={translate("users.fields.roles")} name={["roles"]} rules={[{ required: true }]} getValueProps={(value: any[]) => { return { value: value?.map((item) => String(item?.id)), }; }} getValueFromEvent={(selected: string[]) => { return selected?.map((item) => ({ id: Number(item) })); }} > <Select mode="multiple" {...rolesSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" /> </Form.Item> <Form.Item label={translate("users.fields.plant")} name={["plant", "id"]} getValueProps={(value: any) => { return { value: value ? String(value) : null, }; }} getValueFromEvent={(selected: string) => { return selected ? Number(selected) : null; }} > <Select {...plantSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" /> </Form.Item>
kapa.ai
kapa.ai10mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- I'm glad you found a solution! Your approach of using getValueProps and getValueFromEvent to handle the data type mismatch between the defaultValue and the value of the options in the Select component is indeed a good solution. Here's your solution for reference:
<Form.Item
label={translate("users.fields.roles")}
name={["roles"]}
rules={[{ required: true }]}
getValueProps={(value: any[]) => {
return {
value: value?.map((item) => String(item?.id)),
};
}}
getValueFromEvent={(selected: string[]) => {
return selected?.map((item) => ({ id: Number(item) }));
}}
>
<Select mode="multiple" {...rolesSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" />
</Form.Item>
<Form.Item
label={translate("users.fields.plant")}
name={["plant", "id"]}
getValueProps={(value: any) => {
return {
value: value ? String(value) : null,
};
}}
getValueFromEvent={(selected: string) => {
return selected ? Number(selected) : null;
}}
>
<Select {...plantSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" />
</Form.Item>
<Form.Item
label={translate("users.fields.roles")}
name={["roles"]}
rules={[{ required: true }]}
getValueProps={(value: any[]) => {
return {
value: value?.map((item) => String(item?.id)),
};
}}
getValueFromEvent={(selected: string[]) => {
return selected?.map((item) => ({ id: Number(item) }));
}}
>
<Select mode="multiple" {...rolesSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" />
</Form.Item>
<Form.Item
label={translate("users.fields.plant")}
name={["plant", "id"]}
getValueProps={(value: any) => {
return {
value: value ? String(value) : null,
};
}}
getValueFromEvent={(selected: string) => {
return selected ? Number(selected) : null;
}}
>
<Select {...plantSelectProps} allowClear onSearch={undefined} filterOption={true} optionFilterProp="label" />
</Form.Item>
In this solution, getValueProps is used to convert the value to a string before it's passed to the Select component, and getValueFromEvent is used to convert the selected value back to a number when it's returned from the Select component. This ensures that the defaultValue matches the value of the options in the Select component, preventing the issue of blank tags. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai10mo ago
<@940119979334967366> kapa.ai is still learning and improving, please let us know how it did by reacting below