Svelte中的表单验证

446 阅读6分钟

表单仍然是用户与网络交互方式的一个组成部分。在处理表单时,我们必须处理跟踪用户输入,验证和显示错误,以及处理表单的提交。

在这篇文章中,我们将学习输入绑定在Svelte中是如何工作的,如何用Yup验证表单,以及svelte-forms-lib ,使管理表单更容易。我们还将建立一个样本表单,并使用这些工具对其进行验证,以展示在Svelte中建立和验证表单时的多种选择。

了解Svelte的输入绑定

我们需要一种方法来跟踪和存储用户键入时的输入字段的值。Svelte提供了两个指令来实现这一点:on:inputbind

on:input

每当有输入事件发生时,这个事件监听器就会被调用。

<script>
  let email = "";
  const handleInput = (e) => {
    email = e.target.value;
  };
</script>

<input type="email" name="email" on:input={handleInput} />
<p>{email}</p>

在上面的代码中,我们定义了一个handleInput ,并将其传递给电子邮件输入栏。每当用户输入时,email 变量就会被更新为该字段的值。

bind:value

bind 指令是在Svelte中追踪表单值的一种更简洁的工作方式。

<input type="email" name="email" bind:value={email} />

我们不需要创建一个handleInput 事件,也不需要为给定表单中的每个输入字段设置event.target.value ,而是由bind 为我们处理,每当我们填写输入时,email 变量就会更新。

在本文中,我们将使用bind 指令来跟踪和存储表单值,因为它是一种更简单的工作方式。

用Yup进行验证

Yup是一个JavaScript对象模式验证器。Yup确保对象中的数据处于我们想要的形式和形状。

import * as yup from 'yup';

let values = {
    email: "",
    password: "",
    confirmPassword: "",
    hobby: "",
    checkbox: false,
};

const schema = yup.object().shape({
  email: yup.string().required("Email is required")
    .email("Email is invalid"),
  password: yup.string().required("Password is required"),
  confirmPassword: yup.string().required("Please confirm your password")
    .oneOf([yup.ref("password"), null], "Passwords do not match"),
  hobby: yup.string().required("Hobby is required"),
  checkbox: yup.bool().required("Checkbox must be accepted")
    .oneOf([true], "Checkbox must be accepted"),
});

const validationResult = schema
    .validate(values, { abortEarly: false })
    .then(() => {
      alert(JSON.stringify(values, null, 2));
    })
    .catch((err) => {
      console.log(err.errors);
    });

    //console.log message 
    //[ "Email is invalid", "Passwords do not match", "Hobby is required", "Che//ckbox must be accepted" ]

schema ,我们定义了我们希望我们的表单值的数据是什么样子。这可以确保发送到服务器的数据是有效的。

我们在Yup中使用其validate 方法来验证对象。我们可以在我们定义的任何模式上调用这个方法。

创建一个配置文件表单

现在我们明白了表单绑定在Svelte中是如何工作的,以及Yup是如何验证对象值的,让我们来设置一个配置文件表单的样本并对其进行验证。

<script>
  import schema from './schema';
  let values = {
    //store form data that will then be validated
  };

  const handleSubmit = () => {
    //validate form and submit data
  };
</script>

<div class="container">
  <h1>Profile Form</h1>
  <form on:submit|preventDefault={handleSubmit}>
    <div>
      <input type="text" name="email" bind:value={values.email} 
        placeholder="Email"
      />
    </div>
    <div>
      <input type="password" name="password" bind:value={values.password}
        placeholder="Password"
      />
    </div>
    <div>
      <input type="password" name="confirmPassword"
        bind:value={values.confirmPassword}
        placeholder="Confirm password"
      />
    </div>
    <div>
      <select name="hobby" bind:value={values.hobby}>
        <option value="">Select a hobby</option>
        <option value="Eating">Eating</option>
        <option value="Reading">Reading</option>
        <option value="Sleeping">Sleeping</option>
      </select>
    </div>
    <div>
      <label for="checkbox">Check this box</label>
      <input name="checkbox" type="checkbox" bind:checked={values.checkbox} />
    </div>
  </form>
</div>

我们首先建立一个简单的个人资料表格来获取用户的数据。我们把表单字段绑定到一个values 对象。这个对象是我们将存储表单数据的地方。

验证个人资料表格

现在我们已经创建了个人资料表格,我们需要对它进行验证。

与我们将错误记录到控制台时的做法不同,我们要将它们显示给用户看。

<script>
  let errors = {};

  const handleSubmit = async () => {
    try {
      await schema.validate(values, { abortEarly: false });
      alert(JSON.stringify(values, null, 2));
      errors = {};
    } catch (err) {
      errors = err.inner.reduce((acc, err) => {
        return { ...acc, [err.path]: err.message };
      }, {});
    }
  };
</script>

在这个代码块中,我们创建了一个errors 对象,在这里我们将存储从validate 调用得到的错误。然后,我们创建一个异步函数,handleSubmit 。在这里,我们将处理表单的验证和提交。

我们把我们要验证的数据传给这个方法。在这种情况下,我们将验证从表单中收到的values

Validate 可以接受第二个参数,一个选项对象。默认情况下,验证会在第一个错误时返回。要获得所有返回的错误,我们必须将abortEarly 设置为false

如果没有错误,我们就显示表单的值。如果有,我们就显示错误。然而,在我们显示错误之前,我们必须访问它们。

errors = err.inner.reduce((acc, err) => {
  return { ...acc, [err.path]: err.message };
}, {});

为了访问错误,我们在Yup的验证error.inner 数组上循环,并返回一个由字段和它们的错误信息组成的新对象。然后我们用每个相应的输入字段的错误来更新errors 对象。

显示验证错误

现在我们有了保存每个输入字段的错误的errors 对象,我们需要显示它们。

 <div>
      <input type="email" />
      {#if errors.email}
        <span class="error">{errors.email}</span>
      {/if}
    </div>
    <div>
      <input type="password" />
      {#if errors.password}
        <span class="error">{errors.password}</span>
      {/if}
    </div>
    <div>
      <input type="password" />
      {#if errors.confirmPassword}
        <span class="error">{errors.confirmPassword}</span>
      {/if}
    </div>
    <div>
      <select name="hobby" bind:value={values.hobby}>
        <option value="">Select a hobby</option>
        <option value="Eating">Eating</option>
        <option value="Reading">Reading</option>
        <option value="Sleeping">Sleeping</option>
      </select>
      {#if errors.hobby}
        <span class="error">{errors.hobby}</span>
      {/if}
    </div>
    <div>
      <input name="checkbox" type="checkbox" bind:checked={values.checkbox} />
      {#if errors.checkbox}
        <span class="error">{errors.checkbox}</span>
      {/if}
    </div>

我们设置了一个if 块来处理显示错误。如果一个特定字段存在错误,我们就显示该字段的错误。这个CodeSandbox链接包含了本节的代码。

使用验证svelte-forms-lib

Svelte forms lib是一个受Formik启发的库,用于在Svelte项目中轻松构建表单。

你可以通过以下方式安装svelte-forms-lib

npm i svelte-forms-lib

首先,我们从svelte-forms-lib 中导入createForm 函数。

import { createForm } from "svelte-forms-lib";

这个函数是将svelte-forms-lib 集成到表单中的核心部分。

CreateForm 让我们访问有用的表单帮助,如handleChangehandleSubmit ,以及其他。我们将需要这些帮助函数来设置表单。

<script>
  import { createForm } from "svelte-forms-lib";
  const { form, handleChange, handleSubmit } = createForm({
    initialValues: {
      email: "",
      password: "",
      confirmPassword: "",
      hobby: "",
      checkbox: "",
    },
    onSubmit: (values) => {
      alert(JSON.stringify(values));
    },
  });
</script>
<div class="container">
  <h1>Registration Form</h1>
  <form on:submit|preventDefault={handleSubmit}>
    <div>
      <input
        type="text"
        name="email"
        bind:value={$form.email}
        placeholder="Email"
        on:change={handleChange}
      />
    </div>
    <div>
      <input
        type="password"
        name="password"
        bind:value={$form.password}
        placeholder="Password"
        on:change={handleChange}
      />
    </div>
    <div>
      <input
        type="password"
        name="confirmPassword"
        bind:value={$form.confirmPassword}
        placeholder="Confirm password"
        on:change={handleChange}
      />
    </div>
    <div>
      <select name="hobby" bind:value={$form.hobby} on:blur={handleChange}>
        <option value="">Select a hobby</option>
        <option value="Eating">Eating</option>
        <option value="Reading">Reading</option>
        <option value="Sleeping">Sleeping</option>
      </select>
    </div>
    <div>
      <label for="checkbox">Check this box</label>
      <input
        name="checkbox"
        type="checkbox"
        bind:checked={$form.checkbox}
        on:change={handleChange}
      />
    </div>
    <div>
      <button type="submit">Register</button>
    </div>
  </form>
</div>

除了辅助函数之外,svelte-forms-lib 还公开了可观察的值,这些值为我们提供了关于表单当前状态的信息。在这篇文章中,我们将专注于使用formerrors 的可观察值。然而,你可以在这里查看所有可用的观察变量的列表。

我们将一个配置对象作为参数传递给createForm 。在这里,我们定义了表单的initialValues 和处理表单提交的onSubmit 处理程序。

在配置了createForm 之后,我们需要将配置文件表单挂到svelte-forms-lib 上,这样它就可以跟踪表单的值并处理提交。

为了做到这一点,我们将handleSubmit 帮助器传递给form 元素。我们还将handleChange 传递给输入字段,并将bind 它们的值传递给form 观察器。

自定义验证svelte-forms-lib

现在我们知道了如何将svelte-forms-lib 集成到一个表单中,我们需要处理表单验证。

<script>
  import { createForm } from "svelte-forms-lib";
  const { form, errors, handleChange, handleSubmit } = createForm({
    initialValues: {},
    validate: (values) => {
      let errors = {};
      if (!values.email) {
        errors.email = "Email is Required";
      } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) {
        errors.email = "Invalid emaill address";
      }
      if (!values.password) {
        errors["password"] = "password is required";
      }
      if (!values.confirmPassword) {
        errors["confirmPassword"] = "confirm password is required";
      } else if (values.confirmPassword !== values.password) {
        errors["confirmPassword"] = "password does not match";
      }
      if (!values.hobby) {
        errors["hobby"] = "hobby is required";
      }
      if (!values.checkbox) {
        errors.checkbox = "You must accept our terms";
      }
      return errors;
    },
    onSubmit: (values) => {
      alert(JSON.stringify(values));
    },
  });
</script>
<div class="container">
  <h1>Registration Form</h1>
  <form on:submit|preventDefault={handleSubmit}>
    <div>
      <input
        type="text"
        name="email"
        bind:value={$form.email}
        placeholder="Email"
        on:change={handleChange}
      />
      {#if $errors.email}
        <span class="error">{$errors.email}</span>
      {/if}
    </div>
    <div>
      <input
        type="password"
        name="password"
        bind:value={$form.password}
        placeholder="Password"
        on:change={handleChange}
      />
      {#if $errors.password}
        <span class="error">{$errors.password}</span>
      {/if}
    </div>
    <div>
      <input
        type="password"
        name="confirmPassword"
        bind:value={$form.confirmPassword}
        placeholder="Confirm password"
        on:change={handleChange}
      />
      {#if $errors.confirmPassword}
        <span class="error">{$errors.confirmPassword}</span>
      {/if}
    </div>
    <div>
      <select name="hobby" bind:value={$form.hobby} on:blur={handleChange}>
        <option value="">Select a hobby</option>
        <option value="Eating">Eating</option>
        <option value="Reading">Reading</option>
        <option value="Sleeping">Sleeping</option>
      </select>
      {#if $errors.hobby}
        <span class="error">{$errors.hobby}</span>
      {/if}
    </div>
    <div>
      <label for="checkbox">Check this box</label>
      <input
        name="checkbox"
        type="checkbox"
        bind:checked={$form.checkbox}
        on:change={handleChange}
      />
      {#if $errors.checkbox}
        <span class="error">{$errors.checkbox}</span>
      {/if}
    </div>
    <div>
      <button type="submit">Register</button>
    </div>
  </form>
</div>

除了用一个initialValues 对象和一个onSubmit 函数来配置createForm 外,我们还可以添加一个validate 回调来处理表单验证。

在这里,我们检查每个输入字段的状态,并根据这个状态,更新errors 对象。每当任何一个输入字段出现错误,我们就在if 块中显示出来。

邑的验证svelte-forms-lib

虽然我们可以为我们的表单创建一个自定义的验证,但我们也可以选择把这个责任交给Yup。

我们将与刚才创建的schema 验证对象一起工作。

<script>
  import schema from "./schema";
  import { createForm } from "svelte-forms-lib";
  const { form, errors, handleChange, handleSubmit } = createForm({
    initialValues: {
      //initial values here
    },
    validationSchema: schema,
    onSubmit: (values) => {
      alert(JSON.stringify(values));
    },
  });
</script>

//profile form below

Svelte-forms-lib 通过一个validationSchema 道具提供对Yup验证的支持,该道具接收一个模式对象。我们传入我们定义的模式。你可以在这里找到本节的CodeSandbox链接

自定义表单组件svelte-forms-lib

到目前为止,我们不得不向表单传递handleSubmit ,将每个字段绑定到各自的值,并向每个字段传递handleChange

虽然这能完成工作,但svelte-forms-lib 提供了一种更好的、不那么重复的方法来处理表单:自定义组件。

这些组件将减少模板,使表单代码非常简明。

<script>
  import { Form, Field, ErrorMessage, Select } from "svelte-forms-lib";
  import schema from "./schema";
  const formProps = {
    initialValues: {},
    validationSchema: schema,
    onSubmit: (values) => {
      alert(JSON.stringify(values));
    },
  };
</script>
<div class="container">
  <h1>Registration Form</h1>
  <Form {...formProps}>
    <div>
      <Field type="email" name="email" placeholder="Email" />
      <ErrorMessage name="email" />
    </div>
    <div>
      <Field type="password" name="password" placeholder="Password" />
      <ErrorMessage name="password" />
    </div>
    <div>
      <Field type="password" name="confirmPassword" placeholder="Password" />
      <ErrorMessage name="confirmPassword" />
    </div>
    <div>
      <Select name="hobby">
        <option value="">Select a hobby</option>
        <option value="Eating">Eating</option>
        <option value="Reading">Reading</option>
        <option value="Sleeping">Sleeping</option>
      </Select>
      <ErrorMessage name="hobby" />
    </div>
    <div>
      <label for="checkbox">Check this box</label>
      <Field type="checkbox" name="checkbox" />
      <ErrorMessage name="hobby" />
    </div>
    <div>
      <button type="submit">Register</button>
    </div>
  </Form>
</div>

//profile form below

在这里,我们使用了<Form/>,<Field/>,<Select/>, 和<ErrorMessage/> 组件。

我们通过我们定义的formProps 变量将initialValues,onSubmit,validationSchema 传递给<Form/>nametype 是需要的,以便<Field/> 正常工作并呈现适当的输入类型。

对于<ErrorMessage/> ,我们传入我们想要跟踪的输入字段的名称,如果该输入有错误,<ErrorMessage/> 将显示该错误。我们不再需要自己有条件地渲染错误了。

你可以在这里找到本节的CodeSandbox链接。

总结

在Svelte中创建表单既简单又非常复杂。在这篇文章中,我们已经了解了如何在Svelte中跟踪和存储输入值,如何用Yup处理验证,svelte-forms-lib ,以及我们可以通过不同的方式将这个强大的库集成到我们的表单中。

The postForm validation in Svelteappeared first onLogRocket Blog.