vue -- how to write custom components

Posted by daveyboy on Tue, 10 Dec 2019 08:28:23 +0100

I just started to learn a note of vue. I have nothing to do to turn it out and look at it. By the way, I'd like to organize it. I hope it can help the students who just started to learn vue.

First look at the basic functions of a mature form component
html structure:

<el-form :model="model" :rules="rules">
    <el-form-item label="User name" prop="username">
        <el-input v-model="model.username" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item label="Password" prop="password">
        <el-input type="password" v-model="model.password" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item>
        <el-button type="primary" @click="submitForm('loginForm')">Submission</el-button>
    </el-form-item>
</el-form>

vue Code:
Define data model and validation rules in vue data

  data() {
    return {
      model: { username: "", password: "" },
      rules: {
        username: [
          { required: true, message: "enter one user name" }
        ],
        password: [
          { required: true, message: "Please input a password" },
           {min: 6,max:12,message:'Please input 6~12 Password'}
        ],
      }
    };
  },

This is the form component of elementUI, and there is not much nonsense. Now we will imitate this usage to implement our own form component.
Observe the html structure above, which is divided into the outermost form, as well as the inner form item and input.
form is mainly used to receive binding data model and validation rules.
Form item is used for verification and error prompt.
input is a common label, which is bound to the data model in both directions.

Don't write too much nonsense. Put the code directly
App.vue Code:

<template>
  <div id="app">
    <template>
      <!--Imitation elementUI How to use-->
      <my-form :model="model" :rules="rules" >
        <my-form-item label="User name" prop="username">
          <my-input v-model="model.username"></my-input>
        </my-form-item>
        <my-form-item label="Password" prop="password">
          <my-input v-model="model.password" type="password"></my-input>
        </my-form-item>
      </my-form>
    </template>
  </div>
</template>

<script>
import MyInput from './components/Input.vue';
import MyFormItem from './components/FormItem.vue';
import MyForm from './components/Form.vue';
export default {
  name: "app",
  components: {
    MyInput,
    MyFormItem,
    MyForm
  },
  data() {
    return {
      // data model
      model: { username: "", password: "" },
      // Check rule
      rules: {
        username: [
           {required: true, message: "enter one user name" }
        ],
        password: [
           {required: true, message: "Please input a password" },
           {min: 6,max:12,message:'Please input 6~12 Password'}
        ],
      }
    };
  }
};
</script>

Custom components are used in a way that mimics the elementUI. So it is also implemented internally in this way.
The first thing to implement is custom input.

The code of the Input.vue file is as follows:

<template>
    <div>
        <input :type="type" :value="value" @input="onInput">
    </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      default: ""
    },
    type: {
      type: String,
      default: "text"
    }
  },
  methods: {
    onInput(e) {
      let value = e.target.value;
      this.$emit("input", value);
      this.$parent.$emit("validate");
    }
  }
};
</script>

<style scoped>
</style>

You can see that the internal part of the custom input component is a common input, and what we need to do is to implement two-way binding for this input.

  • The value in props is the value passed in by the v-model bidirectional binding in the parent component.
  • The type in props is the type of the bound input box, text and password. And bind the value to the input tag.
  • On the input tag: value is bound to the value passed in by props. That is to say, if the custom input data model changes, the value in the input input box can also be updated in time.
  • If the input event on the input tag changes, the onInput method will be executed, and the onInput function will trigger the input event, so that the data model will also change, which realizes two-way binding.

The onInput method is triggered when the value of the input tag changes, and then obtains the changed value, triggers the input event of the custom component, passes the new value as a parameter, and notifies the data model to update. In addition, the validate method in the parent component is triggered to notify the upper level component form item, "my value has changed, please verify again".

The FormItem.vue file code is as follows:

<template>
    <div>
        <label>{{label}}</label>
        <div>
            <slot></slot>
            <p v-if="errStatus">{{errMessage}}</p>
        </div>
    </div>
</template>

<script>
import Schema from "async-validator";
export default {
  inject: ["myForm"],
  props: ["label", "prop"],
  data() {
    return {
      errMessage: "",
      errStatus: false
    };
  },
  mounted() {
    // Listen to the events triggered in the lower level input component
    this.$on("validate", this.validator);
  },
  methods: {
    // Verification method
    validator() {
      const rules = this.myForm.rules[this.prop];
      const value = this.myForm.model[this.prop];

      // Description object
      const descriptor = { [this.prop]: rules };
      const schema = new Schema(descriptor);
      schema.validate({ [this.prop]: value }, errors => {
        if (errors) {
          this.errMessage = errors[0].message;
          this.errStatus = true;
        } else {
          this.errMessage = "";
          this.errStatus = "";
        }
      });
    }
  }
};
</script>

First, the input component slot is reserved in the html structure.
Get the title (label) and verification field passed in in props.
The core of the form item component is the validator method. First, the validation rules and data model on the form component need to be obtained inside the method. Here, through the provide input option, this is the new content of vue2.2. (this pair of options need to be used together to allow an ancestor component to inject a dependency into all its descendants, no matter how deep the component level is, and it will always take effect when the upstream / downstream relationship is established (go to the official documents for reference without explanation). Here, the myForm we input is a variable provided in the form component. If we inject an instance of the form component, can we get the validation rules and data model (both are already in the form component instance). You can get the corresponding values and the rules in the validation rule table by using the field names in the data model that needs to be validated at this.prop.
In the verification part, a third-party library is introduced for verification. What needs to be done at this time is to construct an object for initializing the Schema. The format is {field name: rule}, such as {[this.prop]: rules}; expanding this.prop with brackets is to use the value of this.prop as the key of the object.
The next step is to call the validate method for verification.

The Form.vue file code is as follows:

<template>
    <form>
        <slot></slot>
    </form>
</template>

<script>
    export default {
        provide(){
            return {
                myForm: this
            }
        },
        props:{
            model:{
                type:Object,
                required:true
            },
            rules:{
                type:Object
            }
        }
    }
</script>

In the html structure, the slots of the form item component are reserved. props receives the data model and validation rules, and then uses the provide option to pass in its own instance for the subset to use to access the data model and validation rules.

To this simple custom form component.

Topics: Javascript Vue