Bashのhistory機能、便利ですよね?
Webのフォームにも、実装できたら便利なはず
ということで実装していきます
まずはBashのhistoryの挙動を確認
挙動1
↑キーで1つ前、↓キーで1つ後に実行したコマンドを表示できる
挙動2
実行前の編集中のコマンドもhistoryに反映される
挙動3
コマンドを実行するたび、編集中だったコマンドはリセットされる
実装の流れ
- まずは↑キーと↓キーで過去の入力値を表示できるようにする(Enterキーで入力値の確定をする)
- 次に編集時の挙動を実装する
↑キー、↓キーとEnterキーの挙動を実装
idつきのinputタグを用意
history.html
<input id="form" placeholder="some text">
何はともあれ、まずは v-model
history.html
<input
id="form"
placeholder="some text"
v-model="text"
>
キーボードイベントのバインディング
history.html
<input
~ 省略 ~
v-on:keydown.arrow-up.prevent="backHist"
v-on:keydown.arrow-down.prevent="headHist"
v-on:keydown.enter='confirm'
>
※ prevent
でデフォルトの挙動を無効化しないと、カーソルの位置が飛ぶ
Vueインスタンス作成
history.js
const form=new Vue({
el: "#form",
data: {
text: '', // フォームの初期値
index: 0, // ヒストリのインデックス
history: [ '' ] // ヒストリの先頭にもフォームの初期値を入れておく
}
})
※配列の先頭が最新の値とする
↑キー↓キーイベント発火時の関数を定義
history.js
const form=new Vue({
~ 省略 ~
methods: {
backHist(){
// indexはhistoryの長さ未満
(this.index + 1) < this.history.length
? this.index++
: undefined
this.text=this.history[this.index];
},
headHist(){
// indexは0より小さくはならない
(this.index - 1) < 0
? undefined
: this.index--
this.text=this.history[this.index];
},
}
});
Enterキーイベント発火時の関数を定義
history.js
const form=new Vue({
~ 省略 ~
methods: {
~ 省略 ~
confirm: function(){
this.hist[0]=this.text; // 現在の値をヒストリの最新値として格納
// 次のヒストリの初期値を入力(現在のテキストが空のときはヒストリを追加しない)
this.text === ''
? undefined
: this.hist=[ '', ...this.hist ]
this.index=0; // インデックスを0(最新の値)に戻す
this.text=''; // フォームをクリア
}
}
});
これで↑キー、↓キーとEnterキーの挙動は再現できました
編集時の挙動を実装
キー入力イベントのバインディング
history.html
<input
~ 省略 ~
v-on:input="editHist"
>
キー入力されるたびにヒストリが「編集された」と考える
ヒストリにオリジナルのテキストと、編集中のテキストを格納できるようにする
history.js
const form=new Vue({
~ 省略 ~
data: {
~ 省略 ~
history: [{
text: '', // オリジナル
edit: undefined, // 編集中のテキスト(なければundefined)
}]
}
})
編集イベント発火用の関数を定義
history.js
const form=new Vue({
~ 省略 ~
methods: {
~ 省略 ~
editHist: function(){
this.hist[this.index].edit=this.text;
}
}
})
history
の値変更に合わせて confirm
関数を変更する
history.js
const form=new Vue({
~ 省略 ~
methods: {
~ 省略 ~
confirm: function(){
this.hist[0].text=this.text; // 現在の値をヒストリの最新値として格納
// 編集中の値をクリア
this.hist.forEach(
h => h.edit=undefined
);
// 次のヒストリの初期値を入力(現在のテキストが空のときはヒストリを追加しない)
this.text === ''
? undefined
: this.hist=[{
text: '',
edit: undefined
}, ...this.hist ]
// .slice(0, 16) でhistoryの登録数上限(16)を指定することも可能
this.index=0; // インデックスを0(最新の値)に戻す
this.text=''; // フォームをクリア
}
}
})
以上で編集の挙動も実装できました
なぜこれを実装したか
おわりに
簡単に実装できるので、ITエンジニアをターゲットにしたサービスで実装してみてはどうでしょう
About Me
普段はインフラエンジニア
JavaScriptとShellScriptでインフラ自動化ができるOSSを開発しています
- Submarine.js - https://gitlab.com/mjusui/submarine/tree/latest
- Twitter: @submarinejs, @Mjusui