First attach the link address of the project:

Making: github.com/moohng/vali…

motivation

Where there is a form, there is validation, and we use different frameworks with different validation methods. On the PC side, the React Based Ant Design framework has very powerful functions, while on the mobile side, it is difficult to find a complete framework that can compete with it. Popular VUX frameworks on mobile end, although some components come with validation functions, however, it is not very useful, and it is basically difficult to match the needs of real projects.

After abandoning the validation function of vUX components, I found a popular validation library called Vee-Validate on Github. The library is decoupled from form components and does have the flexibility to implement a wide variety of project requirements, but it just feels a bit heavy to me. Generally speaking, the lower the overhead on mobile, the lighter the code, the better. It’s hard to use such a huge tool for just one checksum.

After much thought, it is better to implement a set of verification scheme. First, the verification tool must be completely decoupled from the component. Second, the verification tool must be lightweight enough to be used. Mobile terminal is different from PC terminal. Considering performance, we generally verify forms at the time of submission, and prompt the fields that do not meet the requirements (for example, the field component style becomes red, Toast light prompt, etc.).

thinking

  • Why decouple from components?

    Most of the time, our form components are not really forms, especially in the componentization era brought by React and Vue. Many form components are packaged according to our own needs. It would be painful to bring full validation functionality with each component package, and it is sometimes difficult for different third-party component libraries to achieve consistency in validation. Therefore, we should validate fields that have nothing to do with the component itself, even though our field values come from the component. This way, our validation tool works with any component, whether it’s a form or not, as long as the component is matched to the validation field. So, we’re actually checking the field.

  • How to more lightweight implementation of field verification?

    The ideal way to do this would be: result = validator(target, rules); . We simply execute the validation function when we submit the form, pass in the set of fields to be validated and a set of validation rules, and get the final validation result. Finally, do some follow-up processing based on the results.

  • How are target sets, validation rules, and validation results defined?

    A form has multiple fields, and the target collection should be a single object containing all fields.

    { name: 'Ming'.age: 16.email: '[email protected]' }
    Copy the code

    Different fields often have different verification rules. Verification rules need to be defined for each field, so they should be an object containing all fields.

    { nameRules:1.ageRules:2.emailRules:3 }
    Copy the code

    For the definition of a rule, we generally need null judgment, whether a string meets a condition, whether a number falls within a specified range, and other special processing. In summary, validation types can be categorized into three categories: null, regular, and custom. Empty judgment is the most common judgment, which is classified as a separate class. Regular expressions can meet most of the verification rules and can be classified into one class. The first two basically satisfy 70 or 80 percent of the business scenarios, so all the remaining validation rules are implemented through custom functions. So the final verification rule for each field is as follows:

    {
        required: true.pattern: / ^ QQ \ $/ w} {2 and 5,
        validator() {
            // TODO...}}Copy the code

    When we get the verification result, we want to know whether the verification result passes, which fields in the verification result do not pass, and the corresponding prompt for each field. Therefore, the check result should be an object containing all failed fields.

    const result = { name: 'Name cannot be empty'.age: 'Must be over 18 years of age' }
    Copy the code

    For the verification result, we may only care about whether the verification is passed or not. It is not easy to get the simple verification result object. Therefore, result should also have a hasError() function to return whether the result was in error, and a first() function to return a message indicating the first error field.

    result.hasError()   // true
    result.first()      // The name cannot be empty
    Copy the code

    Depending on your requirements, other methods of operation may be provided.

implementation

Validation function (Validator)

Ideas:

  • Traverse the rule set, take each rule to verify the corresponding field in the target set;
  • The first isIs emptyValidation, null should be defined as:null,undefined,[],{},' 'And so on;
  • If blank verification fails, record the text message and skip other verification. Otherwise, proceed to the next verification.
  • And then regular check, same thing;
  • Finally, the custom validation function should pass in the current validation value and the entire target collection object (there may be comparisons with other fields, etc.).
function validator(target, rules) {
  const ruleKeys = rules ? Object.keys(rules) : []
  if(! ruleKeys.length)return new Result()
  const results = ruleKeys.reduce((errors, key) = > {
    let value = target[key]
    let tips = null
    const { required, pattern, validate, alias = key, message = 'Please enter the correct one${alias}`, trim = true } = rules[key] || {}
    // Remove the first space of the string
    trim && typeof value === 'string' && (value = value.trim())
    if (typeof value === undefined || value === null| |! value.length ||JSON.stringify(value) === '{}') {
      required && (tips = typeof required === 'string' ? required : ` please enter${alias}`)}else if (pattern && pattern instanceof RegExp && !pattern.test(value)) { // Regular check
      tips = message
    } else if (typeof validate === 'function') { // Customize the verification function
      const res = validate(value, target)
      tips = typeof res === 'string'? res : (! res ? message :null)}returntips ? {... errors, [key]: tips } : { ... errors } }, {})return new Result(results)
}
Copy the code

Verification Result

We see that in the Validator function, we return an instance object of Result. The code is simple:

class Result {
  constructor(errors = {}) {
    Object.assign(this, errors)
  }
  hasError() {
    return Object.keys(this).length > 0
  }
  first(index = 1) {
    return Object.values(this)[index - 1]
  }
  firstKey(index = 1) {
    return Object.keys(this)[index - 1]}}Copy the code

Basically, three methods are extended (added to the prototype) on a normal object.

API

validator

Core validator: Validator (Target: Object, Rules: Object) => result: result.

target

Set of target objects to be verified: {name: ‘Kevin’, age: 18}.

rules

Set of verification rules: {name: rule1, age: rule2}.

  • alias: Field alias. For example: name, age. Output as the default prompt, if ignoredkey.
  • trim: Whether to ignore the space at the beginning and end of the string. The defaulttrue.
  • required: Whether it is necessary. Is a string and is output as a prompt.
  • pattern: regular expression.
  • message: output as verification prompt. If you ignore this command, the default prompt is output.
  • validate: Custom check function,validate(value, target). Returns a string as a message indicating that the verification failed, or returnsbooleanIndicates whether the check succeedsfasleOutput the default prompt.

Result

{name: ‘Name cannot be empty ‘, age:’ Age must be greater than 12 years old ‘}

Methods:

  • result.hasError(): Check whether there is any error (fail).
  • result.first([index: Number]): Indicates the prompt for the first field in the verification result.indexSpecify the number of fields. Default is1.
  • result.firstKey([index: Number]): verifies the first field in the resultkey.indexUse as above.

application

The verification library has been published to the NPM repository and can be downloaded using the NPM or YARN tool.

$ yarn add @moohng/validator
Copy the code

inVueThe use of

<template>
  <x-form>
    <x-input label="Name" v-modal="form.name" />
    <x-upload label="Avatar" v-model="form.avatars" />
    <x-select label="Gender" v-model="form.sex" />
    <x-input-number label="Age" v-model="form.age" />
    <x-button type="submit" @click="onSubmit">submit<x-button>
  </x-form>
</tempalte>

<srcipt>import { validator } from '@moohng/validator' import rules from './rules' export default { data() { return { result: null, form: {} } }, methods: { onSubmit() { this.result = validator(form, rules) if (this.result.hasError()) { this.$toast(this.result.first()) } else { // ... }}}}</script>
Copy the code

If you need to style the corresponding component after the verification error, you can use the response result to do so:

<template>
  <x-form>
    <x-input
      :class="{ 'error': result.name }"
      label="Name"
      v-modal="form.name"
    />
  </x-form>
</tempalte>
Copy the code

A more elegant way to do this is through a custom command, such as some scrolling, focus, out of focus, style switch, etc. What you need to remember is that you’ve got the validation result, it already contains the information you need, it’s reactive (defined in data), and all subsequent processing can be extended by the result object itself.

The last

If you feel good, please support more ~