npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vue-valibot

v3.1.2

Published

Vue3 composables for handling form submit with optional valibot integration

Downloads

408

Readme

vue-valibot

A set of simple Vue3 composables for handling form submit, with optional valibot integration.

Unlike FormKit, VeeValidate and others, keeps things simple and doesn't interfere with neither data storage nor the UI workflow.

Full Typescript support with type inference.

Install

npm install vue-valibot

Use

<script setup lang="ts">
import * as v from "valibot"
import { useForm } from "vue-valibot"

// Store input data as you prefer, such as with Vue reactive or ref.
const fields = reactive({
  name: "",
})

const { form, submit, submitting, errors } = useForm({
  input: fields,
  // Schema is optional, but usually recommended.
  schema: v.object({
    name: v.pipe(v.string(), v.trim(), v.minLength(1, "Please enter your name.")),
  }),
  async submit(input) {
    // Input is validated against the schema and typed accordingly.
    const res = await api.post(input)
    if (!res) {
      // errors is valibot.FlatErrors ref typed with schema fields.
      errors.value = { root: ["Failed to submit."] }
    }
  },
})
</script>

<template>
  <form ref="form" @submit.prevent="submit">
    <!-- No fancy syntax for input fields, just use what you prefer. -->
    Name: <input v-model="fields.name" />

    <!-- Field errors. -->
    <div v-for="error in errors?.nested?.name">{{ error }}</div>

    <button type="submit" :disabled="submitting">Submit</button>

    <!-- Form errors. -->
    <div v-for="error in errors?.root">{{ error }}</div>
  </form>
</template>

useForm composable

const {
  // All return values are optional to use.
  form,
  submit,
  submitting,
  submitted,
  errors,
} = useForm({
  // All options are optional to provide.
  input,
  schema,
  submit,
  onErrors,
  // Optional defaults for the return values.
  form,
  submitting,
  submitted,
  errors,
})

useForm options

input

(Optional) Input value, or ref, or a getter, of the data to be validated and/or passed to submit.

schema

(Optional) Valibot schema (or a function that returns a schema, such as when the schema depends on the context).

submit

(Optional) Form submit callback.

Only called if:

  • Form is not being submitted at the moment (submitting.value is falsy).
  • HTML5 validation passes (if enabled).
  • Valibot validation passes (if used).

If input and/or schema were provided, the first argument passed to the submit callback is the (possibly validated) form input. The rest of the arguments are the submit function arguments.

During execution, submitting is true. After successfull execution, submitted is true.

onErrors

(Optional) Error callback.

Called (and awaited) if the validation fails, or if errors.value was set by the submit handler.

form, submitting, submitted, errors

Normally, useForm will create and return these refs (see below), but you may optionally provide your own.

For example, this could be used to share the single submitting flag between multiple forms:

const submitting = ref(false)

const { submit: submit1 } = useForm({
  submitting,
  async submit() { /* ... */ }
})

const { submit: submit2 } = useForm({
  submitting,
  async submit() { /* ... */ }
})

// `submitting` will be true during submit of either form.

useForm shortcut

All the composable options are optional. If the only option you need is submit, there is a shortcut variant:

const { submit, submitting } = useForm(async () => {
  // submitting is true during this callback.
  await api.post()
})

useForm return

form

The form element ref.

Using it with <form ref="form"> will enable HTML5 validation on submit.

submit

The actual form submit function that you should call with something like:

  • <form @submit.prevent="submit">
  • <button @click="submit">

It will:

  • Run HTML5 validation (if the form ref is set).
  • Run valibot validation (if the schema is provided).
  • Call submit callback (if provided).

Arguments passed to this submit function are passed to the submit callback, possibly prepended with form input (if input and/or schema were provided).

During execution, submitting is true. After successfull execution, submitted is true.

submitting

Is the form submit callback executing at the moment?

Use this to disable submit button.

Also, useForm will not perform submit if it sees this is true.

Type: Ref<boolean>.

submitted

Has the form been successfully submitted?

Feel free to reset. useForm doesn't depend on this value.

Type: Ref<boolean>.

errors

Validation errors, either coming from schema validation, or set manually in the submit callback.

Type: Ref<FlatErrors?>.

Submit with arguments

Additional arguments passed to submit composable will be passed to the submit callback after input:

const { submit } = useForm({
  input,
  schema,
  async submit(input, chargeImmediately = false) {
    await api.post({ ...input, chargeImmediately })
  },
})

and then:

<form ref="form" @submit.prevent="submit">
  <!-- Input fields omitted for brevity. -->
  <button type="submit">Submit</button>
  <button type="button" @click="submit(true)">
    Submit and Charge Immediately
  </button>
</form>

If there was no input composable option, all arguments are passed as is:

const { submit, submitting } = useForm(
  async (arg1: number, arg2: string, arg3 = false) => {
    // Note: no `input` argument.
    await api.post({ arg1, arg2, arg3 })
  },
)

// Arguments are type checked:
submit(10, "foo")
submit(20, "bar", true)

Custom submit errors

You can set errors inside the submit handler. This will be treated the same way as if errors were produced by the schema.

In particular, this could be used together with onError:

const { submit, errors } = useForm({
  input,
  schema,
  submit(input) {
    if (!validateInput(input)) {
      errors.value = { root: ["Input is invalid."] }
    }
  },
  onErrors(errors) {
    // errors is valibot.FlatErrors (coming either from validation or from submit handler)
    // TODO: show some alert box.
    console.error(errors)
  },
})

useParse

useParse reactively runs Valibot validation on every input change.

It could be used together with useForm or independently.

<script setup lang="ts">
const input = reactive({
  age: "" as number | "", // for v-input
})

const { errors: presubmitErrors } = useParse({
  input,
  schema: v.object({
    age: v.number(),
  })
})
</script>

<template>
  <form @submit="...">
    Age: <input v-model.number="age" type="number">
    <button type="submit" :disabled="!presubmitErrors">Submit</button>
  </form>
</template>