はじめに
この論文のアイデアは、エレメントのソースコードを読み、それに対応する機能を向上させるためにコンポーネントを段階的に記述するプロセスを自動化することです。
これはInputNumberの研究の続きです。
基本実装
基本的な準備の後、基本的な実現が始まります。
コードをテストしてください:
<el-input-number v-model="num" @change="handleChange" :min="1" :max="10" label="説明 テキスト"></el-input-number>
上位コンポーネントコード:
<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>
アッパー効果:
今回は、Inputコンポーネントを再利用し、加算と減算のロジックを実現するために、両側に2つのボタンを追加します。そして、最大値と最小値を制御し、ボタンを無効にすれば、基本的な実装は完了です。
ボタンクリックで連続増減
プラス/マイナスボタンをクリックすると、マウスを持ち上げなくても値が増減し続けるという機能が追加されました。
これを実現するために、ソースコードはディレクティブカスタム命令を使用しています。これは、連続クリック効果を得るためにマウスダウンイベントのスロットルに依存しています。
カスタムコマンドの作成
import { once, on } from '../utils/dom';
export default {
bind(el, binding, vnode) {
let interval = null;
let startTime;
// binding.expression decrease/increase イベント名
// 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);
// スロットリング
interval = setInterval(handler, 100);
});
}
};
コンポーネントでカスタムコマンドを使用します:
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>
無効ステータス
仕上げ
ステップ
コードをテストしてください:
<el-input-number v-model="num" :step="2"></el-input-number>
コンポーネントにstepプロパティを追加します。このようにコンポーネントのコードに+/-1を置き換えて、+/- this.stepとします。
ステップ
step-strictly属性はブール値を受け付けます。この属性がtrueに設定されている場合、ステップ数の倍数のみが入力可能です。
コードをテストしてください:
<el-input-number v-model="num" :step="2" step-strictly></el-input-number>
厳密なステップカウントを実現するために、直接入力の値がstepの倍数であるかどうかをチェックし、そうでない場合はstepの倍数に置き換えます。これは、InputNumberの値を内部のel-inputに直接バインドしてもできません。まず、el-inputの入力イベントに入力値を記録します。次にchangeイベントでvalueに値を与え、最後にwatch.valueで値をチェックし、stepの倍数に変換します。
data() {
return {
currentValue: 0, // キャッシュ入力値
userInput: null, // 現 の入力値をキャッシュする
};
},
<el-input
ref="input"
:disabled="disabled"
:value="currentValue" // バインディングへの変更currentValue
@input="handleInput"
@change="handleInputChange"
>
</el-input>
// el-input要素コンポーネントソースコード研究-InputNumberカウンタ
handleInput(value) {
this.userInput = value;
},
// 変更イベントの値を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: {
// 在watch.value入力値をチェックサムし、ステップの倍数に変換する。
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);
}
}
},
精度
コードをテストしてください:
<el-input-number v-model="numPrecision" :precision="2" :step="0.1" :max="10"></el-input-number>
ここでステップが小数になり、蓄積の過程で、精度の問題が発生するように0.1 + 0.2になります。要素の解決策は、精度の値を計算倍に展開し、結果を取得し、精度の倍で割ることです。
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 0.1PrecisionFactorは10である。
const precisionFactor = Math.pow(10, this.numPrecision);
return this.toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor);
},
// 結果を確実に計算する0.1このエラー状態は解消される
toPrecision(num, precision) {
if (precision === undefined) precision = this.numPrecision;
return parseFloat(Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision));
},
表示するときは、toFixed関数を使って精度を表示するだけです。
その効果は以下の通り:
寸法
size属性を追加するには、ルート要素のスタイルに size ? 'el-input-number--' + size : ''.
その効果は以下の通り:
ボタンの位置
controls-positionプロパティを設定すると、ボタンの位置が制御されます。
コードをテストしてください:
<el-input-number v-model="num" controls-position="right" @change="handleChange" :min="1" :max="10"></el-input-number>
controls-position='right'でコンポーネント内のスタイルを制御するだけです。
その効果は以下の通り:
概要
厳密なステップと正確さという2つの機能のロジックは少し複雑で、もう少し研究が必要です。
その他のコンポーネントのソースコード調査:
エレメント・コンポーネントのソースコード調査 - レイアウト、リンク、ラジオ
要素コンポーネントソースコードリサーチ-チェックボックス複数選択ボックス
要素コンポーネントのソースコード research-InputNumber counter