Form forms are common in everyday business, and ElementUI is used as a component development when using Vue; In addition to the general form components provided by UI components, such as :input, select, etc., due to business differences, not limited to simple input, selection. There will be combined operations that need to process input/selected values according to business logic. It is also important to explore the implementation of various UI components and incorporate custom form components into the form form.

Each framework, component version in the example

Vue@2.6
Element@2.141.
Copy the code

Reading this article you can learn:

  1. The ElementUI Form is basically used, and the form validates the flow.
  2. V-model custom components and validation with Element from.

ElementUI

Here’s a basic example:

elementForm.vue

<template>
    <div style="width:40%">
        <el-form ref="form" :model="userInfo" :rules="rules">
            <el-form-item label="Name" prop="name">
                <el-input v-model="userInfo.name" placeholder="Name" maxlength="15"></el-input>
            </el-form-item>
            <el-form-item label="Age" prop="age">
                <el-input v-model="userInfo.age" placeholder="Age" ></el-input>
            </el-form-item>
            <el-form-item label="Gender" prop="gender">
                <el-radio-group v-model="userInfo.gender">
                    <el-radio label="0">male</el-radio>
                    <el-radio label="1">female</el-radio>
                </el-radio-group>
            </el-form-item>
            <el-form-item label="Hobby" prop="hobby">
                <el-checkbox-group v-model="userInfo.hobby">
                    <el-checkbox v-for="item in hobbies" :key="item" :label="item" name="hobby"></el-checkbox>
                </el-checkbox-group>
                <div>
                    <el-input v-model.trim="hobby" placeholder="Custom" @change="handleAddHooby" size="mini" style="width:140px;"></el-input>
                </div>
            </el-form-item>
            <el-form-item label="Birthday" prop="birthday">
                <el-date-picker v-model="userInfo.birthday"></el-date-picker>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="handleSubmitInfo">submit</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>
<script>
export default {
    data(){
        function validateAge(rule,value,callback){
            try{
                if(value===' '){
                    callback('Please enter age')}if(! value.match(/^\d+$/)){
                    callback('Please enter a number')}if(value*1 <0 || value*1 >150){
                    callback('Please enter a reasonable age value')
                }
                callback()
            }catch(err){
                console.log(err)
                callback('Please enter age')}}return {
            hobby:"".hobbies: ['basketball'.'read'.'games'.'singing'.'dancing'].userInfo: {name:' '.age:' '.gender:'1'.hobby: [].birthday:' ',},rules: {name: [{required:true.message:'Please enter your name'}].age: [{validator:validateAge,trigger:'change'}}}},methods: {handleAddHooby(){
            if(this.hobbies.includes(this.hobby)){
                return
            }
            this.hobbies.push(this.hobby)
            this.hobby = ' '
        },
        handleSubmitInfo(){
            console.log(this.userInfo)
            this.$refs.form.validate((error,errrorInfo) = >{
                if(error){
                    return}})}}}</script>
Copy the code

Focus:

  • <el-form ref="form" \>Bind to retrieve the form instance and call methods such as validate manual validation and resetFields to reset the form.
  • <el-form-item prop="name" \>Binding, which corresponds to the rules key value of the rule set bound by the form. Verification rules are obtained during verification.
  • <el-input v-model="userInfo.name" \>Component input control, use of V-model instructions
v-modelImplement custom components

It was found that with the component EL-Input, the user could not enter without using the V-Model binding value.

<el-input placeholder="Name"></el-input>
Copy the code

When I looked at the Element source section, I found that the controls were in the code.

setNativeInputValue() {
    const input = this.$refs.input; // this.$refs.input source code is this.getinput ();
    if(! input)return;
    if (input.value === this.nativeInputValue) return;
    input.value = this.nativeInputValue;
},
Copy the code

The value property of the model prop and props defined in the example, so when you use this component at the parent level, If you use both v-model and value, you don’t get the props value in this component (v-model has high priority,value is ignored). You can only get a value in a custom component if you only use it.

custom-input.vue

<template>
    <div>
        <p>This is a custom input component</p>
        <input ref="input" 
            v-model="inputValue" 
            @input="handleInput"
             />
    </div>
</template>
<script>
export default {
    data(){
        return {
            inputValue:' ',}},model: {// Define v-model how to dispose of this component, value attribute definition, event definition
        prop:"value".event:"custom"
    },
    props: {value: [String.Number].// Since the bound property value is the same as the prop defined by the V-model, choose one or the other, v-model takes precedence and value is ignored
    },
    mounted(){
        this.inputValue = this.value
    },
    methods: {/** * If you do not use the v-model, the parent component needs to listen for cusmo events and update the value of value * if you use the V-model, the v-model will listen for events based on the event type defined by the model. Update the value. **/
        handleInput(e){
            console.log(this.inputValue)
            this.$emit('custom'.Math.random()*10); }},}</script>
Copy the code

Custom component references, v-model and value are bound at the same time, value is ignored.

// ... 
<custom-input  v-model="inputValue" value="admin" />
// ...
data(){
    return {
	inputValue:' ',}}Copy the code

Of course, the custom distributed event this.$emit(‘custom’, math.random ()*10); , we can also listen on the parent component

// ... 
<custom-input  v-model="inputValue" @custom='handleVModel' />
// ...
data(){
    return {
	inputValue:' ',}},methods: {handleVModel(val){
      console.log("listener - vModel :",val)
    }
}
Copy the code

Custom input component concerns:

  • Model component property setting, which defines how the V-Model directive handles the current component: data name, event name;

    Prop-value; event – input ; The example defines the event event-custom

    model:{
      prop:"value".event:"custom"
    }
    Copy the code

    You can customize the props\ Event names to handle specific businesses.

  • $emit(‘custom’, event.target.value); $emit(‘custom’, event.target.value);

  • The V-model ignores the default binding values for all form elements, such as value\checked\selected,

  • The name of the model-prop property bound to the V-model is passed to the props of the component. Define the function-value to get the value of the V-Model

Test – Define different model-prop to see how inputs are bound; The value in props can be used to initialize the input value inside the component;

// ... 
model: {// Define v-model how to dispose of this component, value attribute definition, event definition
    prop:"customValue".// Define different prop
    event:"custom"
},
props: {customValue: [String.Number].// The prop value of the bidirectional binding needs to be defined in the props of the component
    value: [String.Number],},watch: {customValue(val){
        console.log(val)
    }
},
mthods: {/** * the internal input box is input to format the internal input data, and the formatted data is given to the V-model, the parent binding is the given value **/
  handleInput(e){
      this.$emit('custom'.Math.random()*10); // The value of the V-model response defined here is given to a random number, distinguishing it from the internal input input}},/ /...
Copy the code

The prop defined by the V-model needs to be defined in the props of the component, which can be viewed by watching the value update print.

Consider: When using a V-Model in a custom form input component, the V-Model is like a higher-order component that internally maintains a property value of the Model-prop name and listens for events of the model-Event name. The event triggers an update of the value of a prop property of its own. When the value is updated, the parent and its own re-rendering are called simultaneously.

el-formCalibration principle

How is validation detected and triggered when a user enters or changes a value

Distribute events to the parent by looking at the source section and listening for the value of value

watch: {
    value(val) {
      // this.$nextTick(this.resizeTextarea);
      if (this.validateEvent) {
        this.dispatch('ElFormItem'.'el.form.change', [val]); // Forward events to el-form-item; Dispatch is an internal custom event forwarding function}}},Copy the code

After Mounted, addValidateEvents is called. After Mounted, addValidateEvents is called. After Mounted, addValidateEvents is called.

addValidateEvents() {
  const rules = this.getRules();

  if (rules.length || this.required ! = =undefined) {
    this.$on('el.form.blur'.this.onFieldBlur);
    this.$on('el.form.change'.this.onFieldChange); // Listen for events to trigger the corresponding callback function}},onFieldChange() {
  if (this.validateDisabled) {
    this.validateDisabled = false;
    return;
  }

  this.validate('change'); // Trigger verification. Change is the value of trigger in the verification rule
},
validate(trigger, callback = noop) {
  this.validateDisabled = false;
  const rules = this.getFilteredRule(trigger); // Merge rules bound to el-Form, rules bound to el-form-item, and Required
  if((! rules || rules.length ===0) && this.required === undefined) {
    callback();
    return true;
  }

  this.validateState = 'validating';

  const descriptor = {};
  if (rules && rules.length > 0) {
    rules.forEach(rule= > {
      delete rule.trigger;
    });
  }
  descriptor[this.prop] = rules; // Check rule set

  const validator = new AsyncValidator(descriptor);  // Reference library async-validator; Initializes the validation rule, instance object Validator
  const model = {};

  model[this.prop] = this.fieldValue; 

  validator.validate(model, { firstFields: true }, (errors, invalidFields) = > { // Call the validAT method to validate the given value model
    this.validateState = ! errors ?'success' : 'error';
    this.validateMessage = errors ? errors[0].message : ' ';

    callback(this.validateMessage, invalidFields);
    this.elForm && this.elForm.$emit('validate'.this.prop, ! errors,this.validateMessage || null); // el-form Any form triggers the verification event, including the current attribute name, verification result, and verification information
  });
},
Copy the code
Custom component combinationel-formcheck

If our custom components need to be integrated into the EL-form, and verify the rule origin. You need to distribute events in a custom component when a value changes.

We need to modify our previously customized component custom-input.vue by distributing validation events to trigger validation when the V-model value changes.

import emitter from 'element-ui/src/mixins/emitter'; // Distribute the el-form method of the event

// ...

mixins:[emitter], // Mix the way to load the current component
watch: {customValue(val){
        console.log(val)
        this.dispatch('ElFormItem'.'el.form.change', [val]); // When the value changes, an event is issued to el-form-item and the validation process inside the component is invoked}},Copy the code

When you’re done, reference it to the parent form component and test it. To see how it works, use the following:

<template>
    <div style="width:40%">
        <el-form ref="form" :model="userInfo" :rules="rules">
            <! - / /... Omit the others -->
            <el-form-item label="Custom" prop="randomValue">
                <custom-input  v-model="userInfo.randomValue" />
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="handleSubmitInfo">submit</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>
<script>
import CustomInput from './custom-input'

export default {
    data(){
        // ... 
        function validateAgeRandomValue(rule,value,callback){ // Use custom validation rules for the values of custom components
            try{
                if(value===' '){
                    callback('Please enter a random number')}if(value<3){
                    callback('Too small')}if(value>7){
                    callback('Too big')
                }
                callback()
            }catch(err){
                callback('Wrong')}}return {
            customInputValue:"".hobby:"".hobbies: ['basketball'.'read'.'games'.'singing'.'dancing'].userInfo: {name:' '.age:' '.gender:'1'.hobby: [].birthday:' '.randomValue:' ',},rules: {name: [{required:true.message:'Please enter your name'}].age: [{validator:validateAge,trigger:'change'}].randomValue: [{required:true.message:'Please enter a random value'}, {validator:validateAgeRandomValue}], // Custom rules}}},components:{
        CustomInput
    },
  	// ...
}
</script>
Copy the code

Testing, done! :white_check_mark:

Originally wanted to continue to write V-Model source code implementation, look very tired, too many things, see the head; Stuck_out_tongue_winking_eye: