Element組件研究-Input輸入框

選擇Input輸入框研究

繼研究了Button組件以後,我又看了一下Link組件的源碼,跟Button組件相似,複雜度不是很高。隨後挑選了Input組件做爲今天的研究對象。git

準備工做

新建InputShownPage頁面,寫測試代碼:算法

<template>
    <div>
        <div class="row">
            <el-input v-model="input" placeholder="請輸入內容"></el-input>
        </div>
    </div>
</template>

<script>
import ElInput from '../../components/Input/index'

export default {
    name: 'InputShownPage',
    methods: {
    },
    components: {
        ElInput
    }
}
</script>
複製代碼

在components文件夾下新建Input組件,接下來咱們實現一個最基礎的Input框。element-ui

編寫基本的Input

在Input組件中,寫bash

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

<script>
  export default {
    name: 'ElInput',
  };
</script>
複製代碼

知識點:$attrs包含了父做用域中不做爲 prop 被識別 (且獲取) 的 attribute 綁定 (class 和 style 除外)app

Input默認會撐滿整行,因此咱們在測試頁InputShownPage爲其添加寬度樣式:測試

.row .el-input {
    width: 180px;
}
複製代碼

效果以下圖:ui

實現組件的v-model綁定

咱們知道v-model由名爲value 的 prop 和名爲 input 的事件組成。即this

<el-input :value="input" @input="(value) => { input = value }"></el-input>
複製代碼

因爲attrs只包含屬性不包含事件,因此在組件裏添加@input="emit('input', $event.target.value)"便可。spa

<input 
    class="el-input__inner"        
    v-bind="$attrs"
    @input="$emit('input', $event.target.value)"
/>
複製代碼

Input Events支持

Element的文檔上Input的事件有blur,focus,change,input,clear。input已經支持,blur,focus,change是原生input標籤就支持的。直接暴露出去便可3d

<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)"
/>
複製代碼

clear是當Input支持clearable屬性的時候的點擊事件,那咱們先讓Input支持clearable屬性。

這樣一個基本的Input組件就完成了。

支持禁用狀態

先寫測試代碼

<el-input
    placeholder="請輸入內容"
    v-model="input"
    :disabled="true">
</el-input>
複製代碼

再寫組件:

  1. 爲組件的props添加disabled: Boolean。
  2. 把組件根div的class改成
:class="[ 'el-input', { 'is-disabled': disabled, } ]"
複製代碼

3,給input標籤添加:disabled="disabled"

效果以下:

可清空

老規矩,先把測試代碼寫在測試頁裏。

<el-input placeholder="請輸入密碼" v-model="input" show-password></el-input>
複製代碼

編寫Input組件:

  1. 增長一個clearable屬性,爲true的時候開啓可清空功能

  2. 增長一個清空按鈕,在mouseover和focus時候且輸入框有值的時候顯示,不然隱藏。

  3. 給清空按鈕添加click相應方法,點擊後清空輸入框的值。具體代碼以下:

<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>
複製代碼

效果以下:

密碼框

老規矩,先把測試代碼寫在測試頁裏。

<el-input placeholder="請輸入密碼" v-model="input" show-password></el-input>
複製代碼

再寫組件代碼:

Element的密碼框,比原生的密碼框多了一個查看密碼的功能,因此實現起來多了個邏輯,默認狀況input的type是passport,點擊查看按鈕,type就變成text。

  1. 增長type屬性,增長showPassword屬性,綁定input的type爲
:type="showPassword ? (passwordVisible ? 'text': 'password') : type"
複製代碼
  1. 增長查看密碼按鈕,並實現其邏輯:
// 爲input加入ref
<input ref="input" ... /> 
 
// 在clear按鈕同級加入查看密碼按鈕
<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;
},
複製代碼

爲此咱們還實現了focus和blur這兩個方法。setSelectionRange的做用是獲取焦點後,可讓光標保持在文本後。

帶 icon 的輸入框

這裏分爲前置icon和後置icon。剛纔的可清除和查看密碼兩個按鈕,樣式上跟後置icon的效果同樣。

這裏就不貼測試代碼了,跟官網上的同樣,分爲屬性和slot兩種方式。

編寫組件代碼:

  1. 添加suffixIcon,prefixIcon兩個屬性。
  2. 添加前置後置兩部分的標籤。
<!-- 前置內容 -->
<span class="el-input__prefix" v-if="$slots.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>
複製代碼
  1. 爲根div添加樣式判斷邏輯。
'el-input--prefix': $slots.prefix || prefixIcon,
'el-input--suffix': $slots.suffix || suffixIcon || clearable || showPassword
複製代碼

效果以下:

文本域

顯然,文本域的type再也不是text,是textarea。因爲textarea沒有先後置內容和元素。且有獨有resize、自適應高度特性。因此在編寫的時候,用一個新標籤作它的呈現。

  1. 編寫標籤。
<template v-if="type !== 'textarea'">
    // input標籤的內容
 </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>
複製代碼

效果以下:

可自適應文本高度的文本域

思路:要實現自適應高度,第一步就是watch文本value,文本變化時,觸發計算文本框的高度,第二步是根據輸入的參數autosize="{ minRows: 2, maxRows: 4}實現計算高度算法。

// 第一步
watch: {
    value() {
        this.$nextTick(this.resizeTextarea);
    },
    type() { // type改變的時候,也須要計算下高度
        this.$nextTick(this.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);
},
複製代碼

calcTextareaHeight不貼了,可在我文檔底部上傳的碼雲查看,也能夠直接差Element源碼,直接搬過來的。

複合型輸入框

寫出測試代碼

<el-input placeholder="請輸入內容" v-model="input">
    <template slot="prepend">Http://</template>
    <template slot="append">.com</template>
</el-input>
複製代碼

在組件中添加對應標籤和樣式便可。

// 給根元素添加樣式
'el-input-group': $slots.prepend || $slots.append,
'el-input-group--append': $slots.append,
'el-input-group--prepend': $slots.prepend,


 <!-- 前置元素 -->
<div class="el-input-group__prepend" v-if="$slots.prepend">
    <slot name="prepend"></slot>
</div>
            
<!-- 後置元素 -->
<div class="el-input-group__append" v-if="$slots.append">
    <slot name="append"></slot>
</div>
複製代碼

效果以下:

尺寸

經過控制樣式便可,給跟標籤添加樣式:

this.size ? 'el-input--' + this.size : '',
複製代碼

輸入長度限制

帶輸入建議 自定義模板 遠程搜索 這三點都是另外一個Input組件el-autocomplete的特性。暫不寫在這裏。先研究輸入長度限制特性,Element的Input組件會有顯示總字數和已寫字數。

// 對於text
<span v-if="isWordLimitVisible" class="el-input__count">
    <span class="el-input__count-inner">
    {{ textLength }}/{{ upperLimit }}
    </span>
</span>

// 對於textarea
<span v-if="isWordLimitVisible && type === 'textarea'" class="el-input__count">{{ textLength }}/{{ upperLimit }}</span>

// 若是showWordLimit爲ture,顯示總字數和已寫字數
isWordLimitVisible() {
    return this.showWordLimit &&
    this.$attrs.maxlength &&
    (this.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;
},

複製代碼

效果以下:

總結

至此,Element的Input組件大部分功能咱們都模仿完畢了。

本文的全部代碼已上傳至碼雲:gitee.com/DaBuChen/my…

相關文章
相關標籤/搜索