说说如何在 Vue.js 中实现数字输入组件

我们对普通输入框进行扩展,实现一个可快捷输入数字组件。

首先制定规则:

  • 只能输入数字。
  • 设计两个快捷按钮,可直接在当前值的基础上增 1 或者减 1。
  • 数字输入组件可设置初始值、最大值与最小值。

接着,规划好 API。一个 Vue.js 组件最重要的 3 个部分就是 props、events 以及 slot,我们需要定义这三个部分的命名以及业务规则。这个组件比较简单,所以我们只用到 props 与 events。

1 基础版

html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>数字输入组件</title>
</head>
<body>
<div id="app">
    <number-input v-model="value" :min="0" :max="6"></number-input>
</div>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
<script src="number.js"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            value: 3
        }
    });
</script>
</body>
</html>

这里,我们使用了 v-model,双向绑定了 value。

number.js:

/**
 * 是否为数字
 * @param val
 * @returns {boolean}
 */
function isNum(val) {
    return (/^[0-9]*$/).test(val);
}

/**
 * 数字输入组件
 */
Vue.component('number-input', {
    template: '\
    <div class="number-input">\
        <input \
            type="text"\
            :value="currentVal"\
            @change="change">\
        <button\
            @click="down"\
            :disabled="currentVal<=min">-</button>\
        <button\
            @click="up"\
            :disabled="currentVal >=max">+</button>\
    </div>',
    props: {//校验
        //最大值
        max: {
            type: Number,
            default: Infinity
        },
        //最小值
        min: {
            type: Number,
            default: -Infinity
        },
        //初始值
        value: {
            type: Number,
            default: 0
        }
    },
    data: function () {
        return {
            currentVal: this.value
        }
    },
    watch: {
        currentVal: function (val) {
            console.log("currentVal:" + this.currentVal);
            this.$emit('input',val);
        },
        value: function (val) {//更新 currentVal
            this.update(val);
        }
    },
    methods: {
        /**
         * 更新
         * @param val
         */
        update: function (val) {
            //让输入的值在限定范围内
            if (val > this.max) {
                val = this.max;
            }
            if (val < this.min) {
                val = this.min
            }
            this.currentVal = val;
        },
        /**
         * 减少
         */
        down: function () {
            if (this.currentVal <= this.min) {
                return;
            }
            this.currentVal -= 1;
        },
        /**
         * 增长
         */
        up: function () {
            if (this.currentVal >= this.max) {
                return;
            }
            this.currentVal += 1;
        },
        /**
         * 如果输入的值,
         * @param event
         */
        change: function (event) {
            var val = event.target.value.trim();//获取输入值

            if (isNum(val)) {//赋值 currentVal
                val = Number(val);
                this.currentVal = val;

                //超出限定范围时,规整
                var max = this.max;
                var min = this.min;
                if (val > max) {
                    this.currentVal = max;
                } else if (val < min) {
                    this.currentVal = min;
                }
            } else {//还原为 currentVal
                event.target.value = this.currentVal;
            }
        }
    },
    mounted: function () {
        //对初始值进行范围限定
        this.update(this.value);
    }
});

这里,我们专门定义了一个 number.js,用于定义数字输入组件。

在 number.js 中,我们做了如下工作:

  1. 利用正则表达式 ,定义了一个判断 “是否为数字” 的函数。
  2. 在模板定义中,我们定义了一个输入框以及两个按钮,首先在 input 中绑定了 currentVal 数据以及原生的 change 事件。接着,定义了递增与递减按钮,每个按钮都绑定了相应的事件,还绑定了 disabled 属性,这样当输入的值超出范围时,按钮就会置灰不可用。
  3. 在定义的 change() 方法中,先获取输入值,然后判断是否为数字。如果是数字,则赋值给 currentVal;如果不是数字,则还原为 currentVal,这样可以保证组件的内容始终是数字。
  4. 接着在 props 中,对每一个参数(最大值、最小值、初始值)定义了相应的校验规则。这样就可以在父组件中初始化这个数字输入组件啦O(∩_∩)O~
  5. 因为 Vue.js 组件时单向数据流,所以不能在组件内部修改之前在 props 中定义的 value。我们可以在 data 函数中,声明一个 currentVal,并把 props 中定义的 value 值赋给它。这样就实现了组件初始化时,引用父组件中的值的工作。
  6. 为了当父组件修改了输入值,也要更新子组件中的 currentVal 的功能,我们需要用到 watch 属性。 watch 属性用于监听某个 prop 或者 data,当它们发生变化时,会触发定义在 watch 中的函数。这里我们监听了 currentVal 与 value,监听了 currentVal 是为了将来当内部更新了 currentVal 的场景时,可以同步到 Value,这里起核心作用的是监听 value 值。为了让输入的值在限定范围内,这里封装了一个 update() 函数。
  7. watch 中的监听函数,有两个入参,第一个是新值,第二个是旧值。这里只用到新值。因为监听函数中的 this ,指向的是当前组件实例,所以可以直接调用定义在 methods 中的函数。
  8. 为了在组件初始化时,对初始值进行范围限定,这里在 mounted 挂载函数中,也调用了 update() 函数。

效果:

2 按键支持

当输入框获得焦点时,按下“向上键”时,增长;按下“向上键”时,减少。

这可以利用按键修饰符来实现,我们修改示例中的组件模板:

...
 <input 
            type="text"
            :value="currentVal"
            @change="change"
            @keyup.up="up"
            @keyup.down="down">
...

Vue.js 定义按键别名有这些:

  • .enter
  • .tab
  • .delete(捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

效果:

3 控制步伐

新增一个步伐属性,增长或者减少以步伐值为变化量。之前的示例,步伐为 1。

首先在 props 中,定义一个步伐属性:

//步伐
step: {
    type: Number,
    default: 1
}

然后在增长与减少函数中,使用步伐属性做为变化量:

/**
 * 减少
 */
down: function () {
    if (this.currentVal <= this.min) {
        return;
    }
    this.currentVal -= this.step;
},
/**
 * 增长
 */
up: function () {
    if (this.currentVal >= this.max) {
        return;
    }
    this.currentVal += this.step;
},

最后为组件重新配置参数:

<number-input v-model="value" :min="0" :max="50" :step="5"></number-input>

效果:

本文示例代码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容