preface
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.
In this article we’ll continue to look at inputNumbers.
Basic implementation
After the basic preparation, the basic implementation begins.
Test code:
<el-input-number v-model="num" @change="handleChange" :min="1" : Max ="10" label=" description "></el-input-number>Copy the code
Component code:
<template> <div :class="[ 'el-input-number', ]" > <span class="el-input-number__decrease" role="button" :class="{'is-disabled': minDisabled}" @click="decrease" > <i class="el-icon-minus"></i> </span> <span class="el-input-number__increase" role="button" :class="{'is-disabled': maxDisabled}" @click="increase" > <i class="el-icon-plus"></i> </span> <el-input ref="input" :value="value" @input="value => $emit('input', value)" > </el-input> </div> </template> <script> import ElInput from '.. /Input/index' export default { name: 'ElInputNumber', props: { value: {}, max: { type: Number, default: Infinity }, min: { type: Number, default: -Infinity }, }, computed: { minDisabled() { return this.value - 1 < this.min; }, maxDisabled() { return this.value + 1 > this.max; }, }, methods: { decrease() { if(this.minDisabled) return this.$emit('input', this.value - 1) }, increase() { if(this.maxDisabled) return this.$emit('input', this.value + 1) } }, components: { ElInput } } </script>Copy the code
The effect:
This time, you can reuse the Input component by adding/subtracting two buttons on both sides. Then control the maximum and minimum value, disable button, the basic implementation is complete.
Click the button to keep increasing/decreasing
Add now: The feature that keeps increasing/decreasing values without lifting the mouse when clicking the add/subtract button.
To achieve this function, the source code uses the directive custom directive, which is throttled by mousedown events to achieve the continuous click effect.
Write a custom command:
import { once, on } from '.. /utils/dom'; export default { bind(el, binding, vnode) { let interval = null; let startTime; Const handler = () => const handler = () => vnode.context[binding.expression].apply(); const clear = () => { if (Date.now() - startTime < 100) { handler(); } clearInterval(interval); interval = null; }; on(el, 'mousedown', (e) => { if (e.button ! == 0) return; startTime = Date.now(); once(document, 'mouseup', clear); clearInterval(interval); // Implement throttling interval = setInterval(handler, 100); }); }};Copy the code
Use custom commands in components:
import RepeatClick from '.. /.. /directives/repeat-click'; directives: { repeatClick: RepeatClick }, <span class="el-input-number__decrease" role="button" :class="{'is-disabled': minDisabled}" v-repeat-click="decrease" > <i class="el-icon-minus"></i> </span> <span class="el-input-number__increase" role="button" :class="{'is-disabled': maxDisabled}" v-repeat-click="increase" > <i class="el-icon-plus"></i> </span>Copy the code
Disabled state
- Add {‘ IS-disabled ‘: disabled} to the root node style.
- Add :disabled=”disabled” to the el-input node.
- Add if(this.disabled) return to decrease/increase methods.
Finished effect:
They count
Test code:
<el-input-number v-model="num" :step="2"></el-input-number>
Copy the code
Add the Step property to the component. Replace the +/-1 in the component with +/- this.step.
Strict steps
The step-strictly property accepts a Boolean. If this property is set to true, only multiples of the number of steps can be entered.
Test code:
<el-input-number v-model="num" :step="2" step-strictly></el-input-number>
Copy the code
To be strict with the number of steps, the value we enter directly checks if it is a multiple of step, and if it is not, we change it to a multiple of step. The value of InputNumber cannot be directly bound to the internal el-input. The input value is first recorded in the input event of el-Input. Then, assign the value to value in the change event. Finally, verify the input value on watch.value and convert it to a multiple of step.
Data () {return {currentValue: 0, // Cache the last input value userInput: null, // cache the current input value}; }, <el-input ref="input" :disabled="disabled" :value="currentValue" // become binding currentValue @input="handleInput" @change="handleInputChange" > </el-input> handleInput(value) {this.userInput = value; HandleInputChange (value) {let newVal = value === "? undefined : Number(value); if (! isNaN(newVal) || value === '') { this.setCurrentValue(newVal); } this.userInput = null; }, setCurrentValue(newVal) { const oldVal = this.currentValue; if (newVal >= this.max) newVal = this.max; if (newVal <= this.min) newVal = this.min; if (oldVal === newVal) return; this.userInput = null; this.$emit('input', newVal); this.$emit('change', newVal, oldVal); this.currentValue = newVal; }, watch: {// Check the input value on watch.value and convert it to a multiple of step value: {immediate: true, handler(value) { let newVal = value === undefined ? value : Number(value); If (this.stepstrictly) {newVal = math.round (newVal/this.step) * this.step} if (newVal >= this.max) newVal = this.max; if (newVal <= this.min) newVal = this.min; this.currentValue = newVal; this.userInput = null; this.$emit('input', newVal); }}},Copy the code
precision
Test code:
<el-input-number v-model="numPrecision" :precision="2" :step="0.1" : Max ="10"></el-input-number>Copy the code
Here step becomes a decimal, so in the process of accumulation, there will be a precision problem of 0.1+0.2. Element’s solution is to multiply the value by its precision and divide the result by its precision.
increase() { if(this.maxDisabled || this.disabled) return const value = this.value || 0; const newVal = this._increase(value, this.step); this.setCurrentValue(newVal); }, _increase(val, step) { if (typeof val ! == 'number' && val ! == undefined) return this.currentValue; // Step is 0.1 and precisionFactor is 10. const precisionFactor = Math.pow(10, this.numPrecision); return this.toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor); }, // make sure that the error is eliminated. precision) { if (precision === undefined) precision = this.numPrecision; return parseFloat(Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision)); },Copy the code
In the display, the use of toFixed function to show the accuracy.
The effect is as follows:
size
Add the size attribute to the root element. Add the size? ‘el-input-number–‘ + size: ‘.
The effect is as follows:
Button position
Setting the controls-position property controls the position of the button.
Test code:
<el-input-number v-model="num" controls-position="right" @change="handleChange" :min="1" :max="10"></el-input-number>
Copy the code
You can control styles within the component by using controls-position=’right’.
The effect is as follows:
conclusion
The logic of strict steps and accuracy is a bit more complicated and needs to be studied a bit more.
Source code in the 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