The research idea of this paper is to read Element source code, and then automatically write components step by step to improve their corresponding functions.

Select Input to explore the Input box

After studying the Button component, I looked at the source of the Link component, similar to the Button component, the complexity is not very high. The Input component was then selected for today’s study.

The preparatory work

Create InputShownPage and write test code:

<template> <div> <div class="row"> <el-input V-model ="input" placeholder=" placeholder "></el-input> </div> </div> </template> <script> import ElInput from '.. /.. /components/Input/index' export default { name: 'InputShownPage', methods: { }, components: { ElInput } } </script>Copy the code

Create a new Input component under the Components folder and implement a basic Input box.

Write a basic Input

In the Input component, write

<template>
    <div class="el-input">
        <template>
            <input 
                class="el-input__inner"        
                v-bind="$attrs"
            />
        </template>
    </div>
</template>

<script>
  export default {
    name: 'ElInput',
  };
</script>
Copy the code

$attrs contains attribute bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope.

Input is a full line by default, so we added a width style to the InputShownPage test page:

.row .el-input {
    width: 180px;
}
Copy the code

The effect is shown below:

Implement the V-Model binding of the component

We know that the V-Model consists of a prop named Value and an event named input. namely

<el-input :value="input" @input="(value) => { input = value }"></el-input>
Copy the code

Add @input=”attrs “to the component because attrs only contains attributes but no events, Add @input=”emit(‘input’, $event.target.value)” to the component.

<input 
    class="el-input__inner"        
    v-bind="$attrs"
    @input="$emit('input', $event.target.value)"
/>
Copy the code

Input Events support

The Input events on Element’s document are blur, focus, change, Input, and clear. Input is already supported. Blur, focus, and change are supported by native input tags. Just expose it

<input 
    class="el-input__inner"        
    v-bind="$attrs"
    @input="$emit('input', $event.target.value)"
    @focus="$emit('focus', $event.target.value)"
    @blur="$emit('blur', $event.target.value)"
    @change="$emit('change', $event.target.value)"
/>
Copy the code

Clear is the click event when Input supports clearable, so let’s make Input support clearable first.

This completes the basic Input component.

Disabled status

Write the test code first

<el-input placeholder=" input" :disabled="true"> </el-input>Copy the code

Rewrite component:

  1. Add disabled: Boolean to the props of the component.
  2. Change the class of the component root div to
:class="[
        'el-input',
        {
            'is-disabled': disabled,
        }
        ]"
Copy the code

3, add :disabled=”disabled” to input tag

The effect is as follows:

Can be empty

As usual, write the test code in the test page first.

<el-input placeholder=" please input password "v-model="input" show-password></el-input>Copy the code

Writing an Input component:

  1. Added a clearable attribute to enable clearable when true

  2. – Added an empty button to show the input field when mouseover and Focus has a value, but hide it otherwise.

  3. Add the corresponding click method to the empty button to empty the value of the input field when clicked. The specific code is as follows:

<template> <div :class="[ 'el-input', { 'is-disabled': disabled, 'el-input--suffix': clearable, } ]" @mouseenter="hovering = true" @mouseleave="hovering = false" > <template> <input class="el-input__inner" v-bind="$attrs" :value="value" :disabled="disabled" @input="$emit('input', $event.target.value)" @focus="handleFocus" @blur="handleBlur" @change="$emit('change', $event.target.value)" /> <! <span class="el-input__suffix" V-if ="getSuffixVisible()"> <span class="el-input__suffix-inner"> < I v-if="showClear" class="el-input__icon el-icon-circle-close el-input__clear" @mousedown.prevent @click="clear" ></i> </span> </span> </template> </div> </template> <script> export default { name: 'ElInput', props: { value: [String, Number], disabled: Boolean, clearable: { type: Boolean, default: false }, }, data() { return { hovering: false, focused: false, }; }, computed: { nativeInputValue() { return this.value === null || this.value === undefined ? '' : String(this.value); }, showClear() { return this.clearable && ! this.disabled && (this.focused || this.hovering) && !! this.nativeInputValue; }, }, methods: { handleFocus(event) { this.focused = true; this.$emit('focus', event); }, handleBlur(event) { this.focused = false; this.$emit('blur', event); }, clear() { this.$emit('input', ''); this.$emit('change', ''); this.$emit('clear'); }, getSuffixVisible() { return this.showClear } }, mounted() { }, }; </script>Copy the code

The effect is as follows:

The password

As usual, write the test code in the test page first.

<el-input placeholder=" please input password "v-model="input" show-password></el-input>Copy the code

Write component code again:

Element’s password box, compared with the original password box, has a function to view the password, so it is more logical to implement. By default, the input type is passport. Click the view button, and the type becomes text.

  1. Add the type attribute, add the showPassword attribute, bind the input type to
:type="showPassword ? (passwordVisible ? 'text': 'password') : type"
Copy the code
  1. Add view password button and implement its logic:
Ref <input ref="input"... < I v-if="showPwdVisible" class="el-input__icon el-icon-view el-input__clear" @click="handlePasswordVisible" ></i> handlePasswordVisible() { this.passwordVisible = ! this.passwordVisible; this.focus(); }, focus() { this.getInput().focus(); setTimeout(() => { this.getInput().setSelectionRange(-1,-1); }); }, blur() { this.getInput().blur(); }, getInput() { return this.$refs.input; },Copy the code

We also implemented the focus and blur methods for this purpose. SetSelectionRange is used to keep the cursor behind the text after getting the focus.

Input box with icon

There are pre-icon and post-icon. The two buttons that can clear and view the password just now have the same effect as the icon after the style.

The test code will not be posted here, as on the official website, divided into properties and slots.

Write component code:

  1. Add suffixIcon and prefixIcon attributes.
  2. Add a label with two parts: front and rear.
<! Prepend content -- -- - > < span class = "el - input__prefix" v - if = $slots. The prefix "| | prefixIcon" > < slot name = "prefix" > < / slot > < I class="el-input__icon" v-if="prefixIcon" :class="prefixIcon"> </i> </span> ... <template v-if="! showClear || ! showPwdVisible"> <slot name="suffix"></slot> <i class="el-input__icon" v-if="suffixIcon" :class="suffixIcon"> </i> </template>Copy the code
  1. Add style judgment logic to the root div.
'el-input--prefix': $slots.prefix || prefixIcon,
'el-input--suffix': $slots.suffix || suffixIcon || clearable || showPassword
Copy the code

The effect is as follows:

Text field

Obviously, the type of the text field is no longer text, it is textarea. Because textarea does not enclose content and elements. And has unique resize, adaptive height characteristics. So when you write it, render it with a new tag.

  1. Write labels.
<template v-if="type ! </template> <textarea v-else ref="textarea" class="el-textarea__inner" v-bind="$attrs" :disabled="disabled" :style="textareaStyle" @input="$emit('input', $event.target.value)" @focus="handleFocus" @blur="handleBlur" @change="$emit('change', $event.target.value)" > </textarea>Copy the code

The effect is as follows:

A text field with adaptive text height

To realize the self-adaptive height, the first step is to watch the text value. When the text changes, the height of the text box is triggered to calculate. The second step is to calculate the height algorithm according to the input parameter autosize=”{minRows: 2, maxRows: 4}.

// Watch: {value() {this.resizetextarea); $resizeTextarea; $resizeTextarea; $resizeTextarea; }}, // resizeTextarea() {const {autosize, type} = this; if (type ! == 'textarea') return; if (! autosize) { this.textareaCalcStyle = { minHeight: calcTextareaHeight(this.$refs.textarea).minHeight }; return; } const minRows = autosize.minRows; const maxRows = autosize.maxRows; this.textareaCalcStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows); },Copy the code

CalcTextareaHeight = calcTextareaHeight = calcTextareaHeight = calcTextareaHeight = calcTextareaHeight = calcTextareaHeight

Compound input box

Write test code

<el-input placeholder=" please input the content "V-model ="input"> <template slot="prepend"> http://< /template> <template slot="append">.com</template> </el-input>Copy the code

Add labels and styles to the component.

/ / to the root element to add style 'el - input - group: $slots. The prepend | | $slots. Append,' el - input - group -- append ': $slots.append, 'el-input-group--prepend': $slots.prepend, <! - the front element -- -- > < div class = "el - input - group__prepend" v - if = "$slots. The prepend" > < slot name = "the prepend" > < / slot > < / div > <! $slot.append ">< slot name="append"></slot> </div>Copy the code

The effect is as follows:

size

Add a style to the tag by controlling the style:

this.size ? 'el-input--' + this.size : '',
Copy the code

Input length limitation

Remote search with Input suggestions custom templates are all features of another Input component, El-AutoComplete. I’m not going to write it here. Consider the Input length limitation feature. Element’s Input component displays the total number of words and the number of words written.

// Text <span v-if="isWordLimitVisible" class="el-input__count"> <span class=" el-Input__count-inner "> {{textLength {{upperLimit}} </span> </span> // for textarea <span v-if="isWordLimitVisible && type === 'textarea'" Class ="el-input__count">{{textLength}}/{{upperLimit}}</span> Displays the total number of words and had to write several isWordLimitVisible () {return enclosing showWordLimit && enclosing $attrs. Maxlength && (enclosing type = = = 'text' | | this.type === 'textarea') && ! this.disabled && ! this.showPassword; }, // upperLimit() {return this.$attrs.maxlength; }, // textLength() {if (typeof this.value === 'number') {return String(this.value).length; } return (this.value || '').length; },Copy the code

The effect is as follows:

conclusion

At this point, we’ve modeled most of Element’s Input component.

All code for this article has been uploaded to the code cloud: gitee.com/DaBuChen/my…

Other component source code study:

Element component source research -Button

Element component source research -Input Input box

Element component source research -Layout, Link, Radio

Element component source research -Checkbox multi-checkbox

Element component source research -InputNumber counter

Element component source code study -Loading component

Element component source research -Message component