3

I want to build a form that can be used to manage an option that is stored in the database as a number.

However, if I now enter the item from the database into the hook:

const form = useForm({.
  initialValues: { 
     option: 1
  },
});

Is this not compatible with the Select component, because it works internally only with strings. So in this case the correct value is not selected:

<Select
  label="Option"
  {...form.getInputProps("option")}
/>

Now how can I solve this problem in an elegant way? Is there an API/option for this or do I really need to manually convert each numeric value into a string to make this compatible with Mantine?

Thanks a lot.

Progman
  • 16,827
  • 6
  • 33
  • 48
K. D.
  • 4,041
  • 9
  • 48
  • 72

4 Answers4

2

I believe you can fix this by casting the type of the "value".

This is undocumented, and potentially could break with future releases, but you can do this:


import { Select } from "@mantine/core";
import { useForm } from "@mantine/form";

const data = [
    { value: '', label: 'Select' },
    { value: 2 as any, label: 'New' } // <-- here's the magic
];

export default function MyForm() {
    const form = useForm({
        initialValues: {
            status: ''
        },
    });
    return (
        <form onSubmit={form.onSubmit((values) => console.log(values))}>
            <Select label="Status" placeholder="Status"
                data={data}
                {...form.getInputProps('status')} />
            <Group position="right" mt="md">
                <Button type="submit">Submit</Button>
            </Group>
        </form>
    );
}

I assume that what's happening inside the Select ultimately is some equality checks that will pass with numbers just as well as strings, so you get the correct item selected when it renders, and the correct value out when it submits.

I personally use this with a zodResolver that would complain bitterly about providing strings where it expects a number, and I'm loathe to make my zod schemas more complex to handle this problem.

To be fair, I can appreciate Mantine taking the approach of only supporting strings, since native HTML <select /> elements will always give you strings, and opening the door to anything else is probably a lot of work.

Still, seems like just providing the number value, and keeping typescript happy with as any (or you could do as unknown as string if you're not into the whole brevity thing) works a treat - at least for now!

pete otaqui
  • 1,432
  • 14
  • 11
1

The code below should be self explanatory. If not, please read the @mantine/form package documentation.

import { Select } from "@mantine/core";
import { useForm } from "@mantine/form";

const data = [
  { value: "", label: "Select an option", disabled: true },
  { value: "1", label: "Option 1" },
  { value: "2", label: "Option 2" },
];

export default function MyForm() {
  // create a form with initial values (field: value map)
  // that match the `value`s defined in `data:
  const form = useForm({
    initialValues: {
      status: "",
    },
  });

  // create form submit handler:
  const formOnSubmit = form.onSubmit(
    (values) => console.log(values),
    // optional, as form.getInputProps() can make inputs display the error themselves:
    (errors) => console.error(errors)
  );

  return (
    <form onSubmit={formOnSubmit}>
      <Select
        label="Status"
        placeholder="Status"
        data={data}
        {...form.getInputProps("status")}
      />

      <Group position="right" mt="md">
        <Button type="submit">Submit</Button>
      </Group>
    </form>
  );
}
Paul-Sebastian Manole
  • 2,538
  • 1
  • 32
  • 33
0

I use this code:

import { Select } from "@mantine/core";
import { useForm } from "@mantine/form";

const data = [
    { value: '', label: 'Select' },
    { value: 2, label: 'New' }
];

export default function MyForm() {
    const form = useForm({
        initialValues: {
            status: ''
        },
    });
    return (
        <form onSubmit={form.onSubmit((values) => console.log(values))}>
            <Select label="Status" placeholder="Status"
                data={data}
                {...form.getInputProps('status')} />
            <Group position="right" mt="md">
                <Button type="submit">Submit</Button>
            </Group>
        </form>
    );
}
EToledo
  • 1
  • 1
  • 1
    Some explanation of the code block would be helpful. The questioner would be in a better position to understand how this code works and to highlight the difference in his approach vs the one you are recommending – Hassan Naqvi Sep 15 '22 at 12:59
  • initialValues.status is basically data[n].value, it's as simple as that. – Paul-Sebastian Manole Oct 24 '22 at 22:32
0

You can quite easily build a custom Select component that handles numbers for values. Something like this:

import {Select, SelectProps} from '@mantine/core'
import {FC} from 'react'

interface NumberSelectProps extends Omit<SelectProps, 'value'|'onChange'>{
  value: number,
  onChange: (value: number) => void,
}

const NumberSelect: FC<NumberSelectProps> = props => {
  const {value, onChange, ...otherProps} = props
  return (
    <Select {...otherProps} value={value.toString()} onChange={it => onChange(Number(it) || 0)}/>
  )
}

export default NumberSelect

Then in your form:

<NumberSelect
  label="Option"
  {...form.getInputProps("option")}
/>
jpcuve
  • 11
  • 1
  • 2