Vue.jsのプロジェクトで「クリックしたら編集可能となり、入力値がdataに反映される」という処理を実装する必要がありましたので方法を考えました。
ADs
扱うデータが一つだけで独立している場合は簡単です。
1 2 |
<div v-if="!edit" class="border p-2 bg-light" v-on:click="doEdit">{{ value }}</div> <input v-else type="text" class="form-control" v-model="value" v-on:blur="edit = false" v-focus> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var app = new Vue({ el: '#app', data: { value: 'クリックしてみてね', edit: false, }, directives: { focus: { // ディレクティブ定義 inserted: function (el) { el.focus(); } } }, methods: { doEdit() { this.edit = true; } }); |
divをクリックされたらフラグ(ここではedit)をtrueにし、v-ifで非表示状態となっていたinputが表示されるという仕組みです。inputからフォーカスが外れるとeditがfalseとなり、inputは非表示、divが表示となります。
input表示後フォーカスを当てるためにカスタムディレクティブを使用しています。
と、これだけなら簡単ですが、ここまでシンプルになることはあまりありません。
「クリックすると編集できる」という処理が求められる場合、対象が単一のデータであるケースはほとんどないと思います。
現実的には配列を受け取ってv-forで一覧にし、そのデータを書き換えられるようにする、という使い方が多いのではないでしょうか。
そのような場合、配列に編集状態を判定する値を追加し、その追加した値をフラグとして編集状態の判定をします。
たとえば以下のような配列があったとして
1 2 3 4 5 6 7 8 9 10 |
array1: [{ value: 'ほげほげ' }, { value: 'ふがふが' }, { value: 'ふーばー' } ] |
編集フラグとなる値(ここではvalueChecker)を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
array1: [{ value: 'ほげほげ', valueChecker: false }, { value: 'ふがふが', valueChecker: false }, { value: 'ふーばー', valueChecker: false } ] |
この配列への追加処理はlodashを使うと簡単です。
createdで_.cloneDeepWithを使い、valueCheckerという値を追加します。
1 2 3 4 5 6 7 8 9 10 11 |
created() { var vm = this; //チェック用データを足す var addArray = _.cloneDeepWith(vm.array1, function (val) { if (typeof val.value != 'undefined') { val.valueChecker = false; } }); vm.array1 = addArray; }); }, |
あとは項目が単独だったときと同様に、valueCheckerのtrue/falseによってdivを表示するかinputを表示するかを分岐します。
1 2 3 4 5 6 7 8 9 10 11 12 |
<div class="row py-2 align-items-center" v-for="(item,index) in array1" :key="'array1' + index"> <div class="col-3"> {{ index }}個目のデータ </div> <div class="col-9"> <div v-if="!item.valueChecker" class="border p-2 bg-light" v-on:click="doEditArray1(index)"> {{ item.value }} </div> <input v-else type="text" class="form-control" v-model="item.value" v-on:blur="item.valueChecker = false" v-focus> </div> </div> |
全体のコードは以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
var app = new Vue({ el: '#app', data: { //配列の場合(データを足す) array1: [{ value: 'ほげほげ' }, { value: 'ふがふが' }, { value: 'ふーばー' } ], }, created() { var vm = this; //チェック用データを足す var addArray = _.cloneDeepWith(vm.array1, function (val) { if (typeof val.value != 'undefined') { val.valueChecker = false; } }); vm.array1 = addArray; }, directives: { focus: { // ディレクティブ定義 inserted: function (el) { el.focus(); } } }, methods: { doEditArray1(index) { this.array1[index].valueChecker = true; }, } }); |
さらに実践的な例を考えてみます。
最も頻繁に扱うことになる配列は以下のようにkey:valueが複数組含まれる配列でしょう。商品データやユーザーリスト考えられます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
array2: [{ name: 'なまえです', value: 'あいうえお' }, { name: 'かきくです', value: 'かきくけこ' }, { name: 'さしこです', value: 'さしすせそ' }, { name: 'たろうです', value: 'たちつてと' }, ], |
この場合、各配列にフラグを足すのもいいですが、配列を丸ごと複製し、値をtrue/falseとすることで編集状態を管理する方法を考えました。
再び_.cloneDeepWithを使い、値をすべてfalseにした新しい配列を生成します。
1 2 3 4 5 6 7 8 9 10 |
created() { var vm = this; //チェック用の配列を作る vm.arrayChecker = _.cloneDeepWith(vm.array2, function (val) { if (!_.isObject(val)) { return false; } }); }, |
HTML部分はdivをクリックした際にindexとキー名を引数にもたせ、どの要素が対象かを判定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<div class="row py-2 align-items-center" v-for="(item,index) in array2" :key="'array2' + index"> <div class="col-3"> <div v-if="!arrayChecker[index].name" class="border p-2 bg-light" v-on:click="doEditArray2(index,'name')"> {{ item.name }} </div> <input v-else type="text" class="form-control" v-model="item.name" v-on:blur="arrayChecker[index].name = false" v-focus> </div> <div class="col-9"> <div v-if="!arrayChecker[index].value" class="border p-2 bg-light" v-on:click="doEditArray2(index,'value')"> {{ item.value }} </div> <input v-else type="text" class="form-control" v-model="item.value" v-on:blur="arrayChecker[index].value = false" v-focus> </div> </div> |
クリック時の処理であるdoEditArray2はフラグ用に生成したarrayCheckerの値を書き換えるだけのものです。
1 2 3 |
doEditArray2(index, key) { this.arrayChecker[index][key] = true; } |
実際に動作するサンプルを公開しましたのでソースや挙動などの参考にしてください。
…もうちょっとスマートな方法があるような?
今後の学習課題としましょう。
ADs
コメントはまだありません。