vanjs-form
v1.1.0
Published
Fully typed form state management library (with validation) for VanJS
Maintainers
Readme
VanJS Form
Fully typed form state management library (with validation) for the VanJS framework. If you are
coming
from React, vanjs-form feels similar to react-hook-form.
Install
yarn add vanjs-form vanjs-core yupnpm i -S vanjs-form vanjs-core yupFeatures
- Dead simple
FormAPI to create and manage the form state - Fully typed method parameter and return types
- Supports validation with Yup. Zod and Joi coming soon!
- Built with developer experience (DX) in mind
QuickStart
import van from "vanjs-core";
import { Form, yupValidator } from "vanjs-form";
import * as yup from "yup";
const { div, h1, p, input, label, select, form: formEl, button, br } = van.tags;
function App() {
const form = new Form({
initialValues: {
title: "",
name: "",
age: 0,
gender: ""
},
validator: yupValidator(
yup.object({
title: yup.string().required("Required"),
name: yup.string().required("Required"),
age: yup.number().required("Required").min(18, "At least 18 years"),
gender: yup.string().oneOf(["Male", "Female"], "Valid gender required")
})
),
validationMode: "oninput" // Validation is run on input as well as on submit. Defaults to 'onsubmit'
});
// Watch values when they change in the form fields
const observables = form.watch("name", "title");
// or
const allValues = form.watch();
// Called when form passes validation
const onsubmit = form.handleSubmit((values) => {
console.log({ submittedValues: values });
});
return div(
h1(() => `VanJS Form for ${observables.val.title} ${observables.val.name}`),
formEl(
{ onsubmit: onsubmit },
label("Title"),
div(
label(
input(form.register("title", { type: "radio", value: "Mr." })),
"Mr."
),
label(
input(form.register("title", { type: "radio", value: "Ms." })),
"Ms."
),
label(
input(form.register("title", { type: "radio", value: "Mrs." })),
"Mrs."
)
),
br(),
label("Name"),
input(form.register("name", { autofocus: true })),
br(),
label("Age"),
input(form.register("age", { type: "number", min: "0", max: "100" })),
br(),
label("Gender"),
select(
form.register("gender", { type: "number", min: "0", max: "100" }),
option({ value: "" }, "Select..."),
option({ value: "Male" }, "Male"),
option({ value: "Female" }, "Female")
),
br(),
button({ type: "submit" }, "Submit")
)
);
}
van.add(document.body, App());API Reference
Form
- The
Formclass controls the form state and has methods to manipulate this state - If
validatorparameter is provided, the submit handler is called if validation passes - Use
validationModeto control whether validation should occur on input or only on submit
import { Form } from "vanjs-form";
const form = new Form({
initialValues: Record<string, any>,
validator: (values: Record<string, any>) => Record<string, any> | FormError,
validationMode: "onsubmit" | "oninput" // Defaults to onsubmit
});After the instance is created, you can access the form functions to work with your form.
YupValidator
- The
yupValidatorfunction takes a YupObjectSchemaand returns avalidatorfunction
const form = new Form({
initialValues: {
title: "",
name: "",
age: 0,
gender: ""
},
validator: yupValidator(
yup.object({
title: yup.string().required("Required"),
name: yup.string().required("Required"),
age: yup.number().required("Required").min(18, "At least 18 years"),
gender: yup.string().oneOf(["Male", "Female"], "Valid gender required")
})
)
});Form methods
import { Form } from "vanjs-form";
const form = new Form({
initialValues: Record<string, any>,
validator: (values: Record<string, any>) => Record<string, any> | FormError,
validationMode: "onsubmit" | "oninput" // Defaults to onsubmit
});
form.register();
form.get();
form.set();
form.error();
form.watch();
form.reset();
form.handleSubmit();Register
- Registers an input with the form
- Merges
additionalPropsinto returned object - Returns an object of props (
name,value,checked,oninput,onfocus) which should be assigned as the element's props
const form = new Form({
initialValues: {
name: "Kwame",
age: 28,
foo: true,
location: ""
}
});
return div(
input(form.register("name", { type: "text", placeholder: "Enter your name" })),
input(form.register("age", { type: "number", min: 0, max: 100 })),
input(form.register("foo", { type: "checkbox" })),
select(
form.register("select"),
option({ value: "home" }, "Home"),
option({ value: "work" }, "Work")
)
);Get
- Gets the typed value of the form field with the
namekey
const form = new Form({
initialValues: {
name: "Kwame",
age: 28,
foo: true
}
});
const name = form.get("name");
console.log(name); // "Kwame"
const age = form.get("age");
console.log(age); // 28
const foo = form.get("foo");
console.log(foo); // trueSet
- Sets the value of the form field with the
namekey
const form = new Form({
initialValues: {
name: "Kwame",
age: 28,
foo: true
}
});
form.set("name", "Opare");
form.set("age", 29);
form.set("foo", false);Error
- Gets the error string of the form field with the
namekey
const form = new Form({
initialValues: {
name: "Kwame",
age: 28,
foo: true
},
validator: yupValidator(
yup.object({
name: yup.string().required("Required").min(6, "Must be more than 6 characters"),
age: yup.number().required("Required"),
foo: yup.boolean().required("Required"),
})
),
validationMode: "oninput"
});
const nameError = form.error("name");
console.log(nameError); // "Must be more than 6 characters"
const ageError = form.error("age");
console.log(ageError); // ""
const fooError = form.error("foo");
console.log(fooError); // ""An empty string is returned if the field has no error
Watch
- Returns a reactive object containing the specified field values
- No need to wrap in
van.derive()🙂
const form = new Form({
initialValues: {
name: "Kwame",
age: 28,
foo: true
},
});
const full = form.watch();
const partial = form.watch("name", "age");
return div(
p(() => `Full data: ${JSON.stringify(full)}`), // Renders: Full data { "name": "Kwame", "age": 28, "foo": true }
p(() => `Partial data: ${JSON.stringify(partial)}`), // Renders: Partial data { "name": "Kwame", "age": 28 }
);Errors
- Returns a reactive object containing the specified field errors
- No need to wrap in
van.derive()🙂
const form = new Form({
initialValues: {
name: "Kwame",
age: 28,
foo: true
},
validator: yupValidator(
yup.object({
name: yup.string().required("Required").min(6, "Must be more than 6 characters"),
age: yup.number().required("Required"),
foo: yup.boolean().required("Required"),
})
),
validationMode: "oninput"
});
const errors = form.errors();
const partial = form.errors("name", "age");
return div(
p(() => `Full errors: ${JSON.stringify(errors)}`), // Renders: Full errors { "name": "Must be more than 6 characters", "age": "", "foo": "" }
p(() => `Partial errors: ${JSON.stringify(partial)}`), // Renders: Partial errors { "name": "Must be more than 6 characters", "age": "" }
);Reset
- Resets a form field or the entire form
const form = new Form({
initialValues: {
name: "Kwame",
age: 28,
foo: true
}
});
form.reset("name"); // Reset name field
form.reset("name", "age"); // Reset name and fields
form.reset(); // Reset all fieldsHandle submit
- Register a function to use as a submit handler
- If
validatoris specified on the form, the handler is called if validation passes - Returns a new function to be passed to the form element
const form = new Form({
initialValues: {
name: "Kwame",
age: 28,
foo: true
}
});
const handleSubmit = form.handleSubmit((values) => {
console.log(values);
});
return form(
{ onsubmit: handleSubmit },
// ... Form children
)Contributors
Changelog
- 1.1.0 (Current)
- Added
Form.errors()method
- Added
- 1.0.2
- Disabled call to
form.validateFieldonform.resetto avoid errors on form reset
- Disabled call to
