In this paper, the target

  • Learn to use elementUI’s form components
  • How the component passes values
  • Encapsulate the Input component, and the form component with validation

Let’s take a look at how elementUI’s form component works

Here’s the code copied from elementUI: A basic form form consists of three components: el-form, el-form-item, and el-Input

El-form: receives two parameters: Model receives data from externally transmitted forms, and rules receives verification rules

The el-form-item component displays error information about the label and display validation

3. El-input: bidirectional binding

<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"> <el-form-item Label =" active name" prop="name"> <el-input V-model =" ruleform. name"></el-input> </el-form-item> </el-form>Copy the code

Encapsulating input components

Target: V-model can be used for m-input outside, and send “validation events”

1, custom components to use v-model, must :value / @input

2. When the input event is triggered, a validation event is issued to tell the parent component to validate

V-bind =”$attrs”: passes down non-props features (except class and style) bound to the component tag when calling the component. You should add inheritAttrs: False to the child component (to prevent parent-scoped feature bindings that are not recognized as props from being applied to the root element of the child component). Note: $attrs stores parts other than props

The listeners can emit(eventName) events directly on the parent component’s tag, and the listeners can notice the events on the parent component directly on the record

The two properties can handle functions and events passed by the component without writing extra code, just passing $attrs and $Listeners up and down.

<template>
  <div class="m-input">
    <input :value="value" @input="onInput" v-bind="$attrs">
  </div>
</template>
Copy the code
<script>
    export default {
        inheritAttrs: false.// Avoid the top-level container inheriting attributes
        props: {
            value: {
                type: String.default: ' '
            }
            // placeholder doesn't need this props because $attrs is placeholder
        },
        methods: {
            onInput(e) {
                // Notify the parent component of the value change
                this.$emit('input', e.target.value);
                // Notify FormItem validation
                this.$parent.$emit('validate');
            }
        },
    }
</script>
Copy the code

Encapsulates the Form-item component

Goal:

  • Displays information about label and validation errors
  • Leave a slot for input
  • Validates a single item
<template>
  <div class="m-form-item">
    <i class="warn-icon" v-if="isNecessary&&label">*</i>
    <label v-if="label">
      <span>{{label}}</span>
    </label>
    <slot></slot>
    <p class="warning-msg">{{warningMsg}}</p>
  </div>
</template>
Copy the code

A validation library async-Validator is required for this validation. See Github for details

Import Schema from 'async-validator'; const descriptor = { name(rule, value, callback, source, options) { const errors = []; if (! /^[a-z0-9]+$/.test(value)) { errors.push(new Error( util.format('%s must be lowercase alphanumeric characters', rule.field), )); } return errors; }}; const validator = new Schema(descriptor); validator.validate({ name: 'Firstname' }, (errors, fields) => { if (errors) { return handleErrors(errors, fields); } // validation passed });Copy the code

The js part is to accept the data of the current item, and the verification rule of the current item, and finally the verification. But form-item accepts only two arguments: label and prop are strings and do not retrieve form data. Because you can nest multiple layers of forms, you can’t just pass values from parents and pass data in. At this time, it involves several ways of vUE component transmission and communication.

Component transmission and communication

Father = > the son

  • Properties props
  • Reference refs:this.$refs.form.mobile='123'
  • children: this.$children[0].mobile='123'

= > the father

  • Custom events:$emit('name',123)/@name

Brother communication

This.$parent. On (‘name’,123)/this.$parent. Emit (‘name’)

Between ye sun.

Grandparents provide data by providing

provide() {
   return {formData: 123}
}
Copy the code

Inject data by inject[‘formData’]

Any two components

Use Vuex or eventBus

Return to form-item and get the outermost parent form’s data. Therefore, we need the form component to provide the current instance to the grandchild, so form-item can inject the entire form instance

import Schema from "async-validator"

data() {
    return {
      isNecessary: false.// Mandatory
      warningMsg: ' ' // Incorrect verification information}},inject: ['from'].// The data provided by the form
  props: {
    label: String.prop: String
  }
Copy the code

Data and rules have been taken, the following is to verify! Write the validation method as described in the Async-Validator documentation

methods: {
    onValidate() {
      console.log(this.prop) // Rule name for the current item
      console.log(this.form) // The entire form instance: {model: received data, rules: received rules}
      const descriptor = { // Get the rule description here
        [this.prop]: this.from.rules[this.prop]
      }
      const validator = new Schema(descriptor);
      return validator.validate({ [this.prop]: this.from.model[this.prop] }, (errors) = > {
        if (errors) {
          console.log(errors);
          this.warningMsg = errors[0].message
        } else {
        // Clears the error message
          this.warningMsg = ' '}}}})Copy the code

Finally, the validation event is listened for and the validation method is executed

 mounted() {
    if (JSON.stringify(this.from.rules) ! = ='{}') {
      this.isNecessary = true
    }
    this.$on('validate'.() = > {
      this.onValidate()
    })
  },
Copy the code

Encapsulating form components

The form component does two things: 1. Receives data and validates rules. 2. Perform global verification. Form-item does the rest, so the page structure is simple. All you need is one slot, and it’s all custom

<template>
  <div>
    <slot></slot>
  </div>
</template>
Copy the code

The JS part deals with two things:

  • Receives external data and an array of validation rules
  • Verify rules globally
provide() {
    return {
      'from': this // Pass the entire instance directly}},props: {
    model: {
      type: Object.required: true
    },
    rules: Object
  }
Copy the code

Return return for all form-items; Note: The result of async-validator return is a Promise.

Just take out all the validation tasks and execute them

methods: { validate(cb) { const tasks = this.$children .filter(item => item.prop) .map(item => item.onValidate()); Console. log(tasks); // Tasks is an array of promises // promise. all can wrap multiple promise instances into a new promise instance. Also, success and failure return different values, success returns an array of results, Promise.all(tasks).then(() => cb(true)).catch(() => cb(false)); }}Copy the code

The basic form is complete, and the usage is consistent with elementUI

When submitting the form, simply call the form’s validate() method with this.$refs to validate the entire form

<template> <div class="home"> <m-form :model="formData" :rules="rules" ref="form"> <m-form-item label=" phone number" Prop ="phone"> <m-input type="text" V-model =" formdata. phone" placeholder=" input number "/> </m-form-item> <m-form-item Label ="password" prop="password"> < M-input type="password" V-model =" formdata. password" /> </ M-form-item > <m-form-item> <div @click="submit"> Submit </div> </m-form> </div> </template> <script> import mForm from '@/components/mForm/index.vue' import mFormItem from '@/components/mForm/Item.vue' import mInput from '@/components/mInput/index.vue' export default { name: 'Home', components: { mForm, mFormItem, mInput }, data() { return { formData: { phone: '', password: '' }, rules: { phone: [ { required: True, message: 'Please input mobile phone number'}], password: [{required: true, message: 'Please input password'}]}}}, methods: { submit() { this.$refs.form.validate((valid) => { console.log(valid); if (valid) { alert('submit! '); } else { alert('error! '); return false; }}); } }, } </script>Copy the code