import { FormControl, FormField, FormItem, FormLabel, FormLabelContent, FormMessage, LabelWithTooltipProps } from "@/components/ui/form";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { cn } from "@/lib/utils";
interface FormFieldSelectProps {
control: any;
name: string;
disabled?: boolean;
className?: string;
options: { value: string; label: string }[];
emptyOption?: { value: string; label: string };
fieldProps?: any;
labelProps: LabelWithTooltipProps;
labelWidth?: string;
}
/**
* A form field select component for selecting options.
*
* @component
* @example
* <FormFieldSelect
* name="propertyType"
* control={form.control}
* disabled={!isPowerUser}
* options={propertyTypeOption.map((propertyType) => ({
* value: propertyType,
* label: capitalize(propertyType),
* }))}
* fieldProps={{
* className: 'w-full',
* }}
* labelProps={{
* label: "Property type",
* isRequired: true,
* tooltipContent: "This helps classify properties.",
* }}
* labelWidth="w-64"
* />
*/
export default function FormFieldSelect({
control,
name,
disabled = false,
options,
emptyOption,
fieldProps = {},
labelProps,
className,
labelWidth = "w-64",
}: FormFieldSelectProps) {
return (
<FormField
control={control}
name={name}
render={({ field, formState }) => (
<FormItem
className={cn("flex gap-10 w-full items-start justify-start", className)}
>
<FormLabel className={cn("flex shrink-0", labelWidth)}>
<FormLabelContent {...labelProps} />
</FormLabel>
<div className="flex flex-col gap-2 w-full">
<Select
key={`select-${name}`}
onValueChange={field.onChange}
defaultValue={field.value}
disabled={disabled}
{...fieldProps}
>
<FormControl>
<SelectTrigger
className="w-full"
error={!!formState.errors[name]}
>
<SelectValue placeholder={"Select an option"} />
</SelectTrigger>
</FormControl>
<SelectContent
key={`select-content-${name}`}
>
{emptyOption && (
<SelectItem
key={`select-empty-${name}`}
value={emptyOption.value}>{emptyOption.label}</SelectItem>
)}
{options.map((option) => (
<SelectItem key={`select-item-${option.value}`} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage className="text-danger" />
</div>
</FormItem>
)}
/>
);
}
该组件使用shadcn/ui的组件。
我在类似的形式中使用formfield:
{/* GENDER */}
<FormFieldSelect
className={cn(formFieldWidth)}
key="genderSelect"
name="gender"
control={form.control}
options={genderOptions.map((genderOption) => ({
value: genderOption.value,
label: capitalize(genderOption.label),
}))}
fieldProps={{
className: 'w-full',
}}
labelProps={{
label: "Gender",
isRequired: false,
}}
labelWidth={formFieldLabelWidth}
/>
genderoptions =我类型库中的usergender:
export const UserGenders = [
{ value: "female", label: "Female" },
{ value: "male", label: "Male" },
{ value: "other", label: "Other" },
{ value: 'N/A', label: "Prefer not to say" },
]
所有效果都很好,直到用户使用浏览器的翻译功能(主要是Chrome中的Google Trad,因为Firefox Translation似乎并没有引起相同的错误)。
翻译功能似乎正在翻译所有客户端(甚至是元数据),这似乎导致了一些错误。
无论如何,用户试图在浏览器翻译页面中操纵FormFieldSelect,都会收到此错误:
uncaught domexception:未能在“节点”上执行“ removechild”:要删除的节点不是此节点的孩子。显然,错误是由于虚拟DOM与实际DOM之间的对帐。 我一直在尝试许多钥匙,使组件成为一个
"client component"
任何想法?aissue似乎来自Google Translate,将NextJS不知道的页面添加元素。
2解决方案(更多解决方法)是在ShadCN问题上提出的:
adding to to htmltag用一个像这样包装元素:
感谢 @
radimvaculik提供帮助!