Saltar al contenido principal

Formulario

Componente compuesto formado por LhForm + LhFormItem, que aporta validación de datos, mensajes de error, alineación de etiquetas y otras capacidades.

Uso básico​

Formulario con validación
import { useRef, useState } from 'react'
import { LhButton, LhForm, LhFormItem, LhInput, LhSelect } from '@lh-ui/react'
import type { LhFormInstance, LhFormRule } from '@lh-ui/react'

interface FormData {
name: string
style: string
}

const rules: Record<string, LhFormRule[]> = {
name: [{ required: true, message: 'Introduce el nombre de la obra', trigger: 'blur' }],
style: [{ required: true, message: 'Selecciona el estilo de pintura', trigger: 'change' }],
}

export default function Demo() {
const [formData, setFormData] = useState<FormData>({ name: '', style: '' })
const formRef = useRef<LhFormInstance>(null)

const handleSubmit = async () => {
const valid = await formRef.current?.validate()
if (valid) alert(`Envío correcto: ${formData.name} / ${formData.style}`)
}

const handleReset = () => {
formRef.current?.resetFields()
setFormData({ name: '', style: '' })
}

return (
<LhForm ref={formRef} model={formData} rules={rules} labelWidth="80px">
<LhFormItem label="Nombre de la obra" prop="name">
<LhInput
value={formData.name}
onChange={(val) => setFormData((prev) => ({ ...prev, name: val }))}
/>
</LhFormItem>
<LhFormItem label="Estilo de pintura" prop="style">
<LhSelect
value={formData.style}
onChange={(val) => setFormData((prev) => ({ ...prev, style: val as string }))}
options={[{ label: 'Tinta china', value: 'ink' }]}
/>
</LhFormItem>
<LhFormItem>
<LhButton type="primary" onClick={handleSubmit}>Enviar</LhButton>
<LhButton onClick={handleReset}>Restablecer</LhButton>
</LhFormItem>
</LhForm>
)
}

Form Props​

PropDescripciónTipoPredeterminado
modelObjeto de datos del formulario (obligatorio)Record<string, unknown>—
rulesReglas de validación; la clave es el nombre del campoRecord<string, LhFormRule[]>—
labelWidthAncho de la etiquetastring | number—
labelPositionPosición de la etiqueta'left' | 'right' | 'top''right'
inlineDisposición en líneabooleanfalse
disabledDesactiva todos los elementos hijosbooleanfalse
sizeTamaño de los elementos hijos'large' | 'middle' | 'small'Sigue ConfigProvider
onSubmitEvento de envío nativo (ya con preventDefault)(event: FormEvent) => void—

FormItem Props​

PropDescripciónTipoPredeterminado
labelTexto de la etiquetastring—
propNombre del campo (admite ruta de puntos; corresponde a la clave de model)string—
rulesReglas a nivel de item (si coinciden, sustituyen a las de nivel form)LhFormRule[]—
requiredAsterisco de obligatorio (solo el marcado visual; la semántica de validación la aportan rules)booleanfalse
labelWidthSustituye al ancho de etiqueta de nivel formstring | number—
errorMessageMensaje de error personalizadostring—
showMessageMuestra el mensaje de errorbooleantrue

LhFormRule​

PropDescripciónTipo
requiredSi es obligatorioboolean
messageMensaje de error de validaciónstring
triggerMomento de disparo'blur' | 'change'
validatorFunción de validación personalizada (puede devolver una Promise; la validación asíncrona se espera correctamente)(value, model) => string | undefined | Promise<string | undefined>
min / maxValor mínimo / máximo (número o longitud)number
patternValidación con expresión regularRegExp

Métodos del ref de Form​

LhFormInstance (expuesto mediante forwardRef + useImperativeHandle):

MétodoDescripciónValor de retorno
validateValida todos los camposPromise<boolean>
validateFieldValida un solo campoPromise<boolean>
resetFieldsRestablece todos los campos a su valor inicialvoid
resetFieldRestablece un solo campovoid
const formRef = useRef<LhFormInstance>(null)
const ok = await formRef.current?.validate()
Validación asíncrona

Esta biblioteca corrige el defecto de validación asíncrona de plus: el validateField de plus usa Promise.resolve(!message), que no espera a la Promise de un validador asíncrono, por lo que la validación asíncrona siempre devuelve true. La versión React espera explícitamente a que se resuelva el thenable antes de juzgar. Consulta la Guía de migración.

Registro de campos

LhFormItem registra su validador en el formulario dentro de useEffect y se da de baja automáticamente al desmontarse. No hace falta declarar los campos en rules de antemano: los campos que existen en model pero no en rules reciben una validación de su valor inicial en el momento del registro.

Semántica de trigger (mejora de React)

La función validateFormField del núcleo compartido no interpreta trigger; el momento de disparo lo decide esta capa: trigger: 'blur' (predeterminado) valida cuando FormItem pierde el foco y trigger: 'change' valida cuando hay un cambio en la zona de contenido. Las reglas que no declaran trigger solo se ejecutan al perder el foco y al llamar a validate(), de modo que no se muestran errores constantemente mientras se escribe.

La validación change toma el nuevo valor del destino del evento, no la instantánea de model: en React, la actualización del estado y el despacho del evento no ocurren en el mismo ciclo, y leer de nuevo model iría un ciclo por detrás. El evento change de los controles nativos (input / textarea / select nativo) se captura al burbujear a la zona de contenido; los controles personalizados no tienen evento change nativo (la pulsación de una opción de LhSelect solo produce un click), por lo que se dispara manualmente con el nuevo valor a través del validateChange(value) del contexto de FormItem.

El restablecimiento borra los errores (mejora de React)

resetFields(), además de devolver model a la instantánea de sus valores iniciales, incrementa el resetVersion interno; cada FormItem detecta el cambio de versión y limpia su propio estado de error. El estado controlado externo debe sincronizarse por separado (como el setFormData(initial) del demo): React no puede escribir model en el lugar de la cadena reactiva de Vue.

Estilos​

import '@lh-ui/react/style/form'