hilla
hilla copied to clipboard
Conditionally rendered form fields are not initialized properly by field directive
Describe the bug
When using a form, any conditionally rendered fields will not be properly initialized by the field
directive. For example the required indicator might be missing, or the field value not set. See the code in Reproduction for more details.
Expected-behavior
When I read an object into a form, and then later render a field using the field
directive, it should be properly initialized, with the required indicator visible (if so configured) and the value set (if present inside the form value).
Reproduction
import {FormLayout} from "@hilla/react-components/FormLayout";
import {useForm, useFormPart} from "@hilla/react-form";
import {useEffect, useState} from "react";
import {
_getPropertyModel,
makeObjectEmptyValueCreator,
NotBlank,
NumberModel,
ObjectModel,
StringModel
} from "@hilla/form";
import {Button} from "@hilla/react-components/Button.js";
import {TextField} from "@hilla/react-components/TextField";
interface ProjectDTO {
id?: number;
name: string;
}
class ProjectDTOModel<T extends ProjectDTO> extends ObjectModel<T> {
static override createEmptyValue = makeObjectEmptyValueCreator(ProjectDTOModel);
get id(): NumberModel {
return this[_getPropertyModel]("id", (parent, key) => new NumberModel(parent, key, true, {meta: {javaType: "java.lang.Long"}}));
}
get name(): StringModel {
return this[_getPropertyModel]("name", (parent, key) => new StringModel(parent, key, true, {meta: {javaType: "java.lang.String"}}));
}
}
export function FormDebugView() {
const {model, field, value, clear, read} = useForm(ProjectDTOModel);
const nameField = useFormPart(model.name);
useEffect(() => {
nameField.addValidator(new NotBlank({message: "Please enter a name."}));
}, []);
const [formVisible, setFormVisible] = useState(false);
function load() {
const ts = Date.now();
read({
id: ts,
name: "Test " + ts
});
}
return <>
<Button onClick={clear}>Clear Form</Button>
<Button onClick={load}>Load Form</Button>
<Button onClick={() => setFormVisible(true)}>Show Form</Button>
<Button onClick={() => setFormVisible(false)}>Hide Form</Button>
{formVisible && (<FormLayout>
<TextField label={"Project name"} {...field(model.name)}/>
</FormLayout>)}
<p>Form value: {JSON.stringify(value)}</p>
</>;
}
Play around with the show/hide and load/clear buttons. You will notice the field showing up empty and the required indicators coming and going.
System Info
macOS 14.1.1, Chrome 119.0.6045.159, Hilla 2.4.0