【Vue.js】配列の監視(Deep)がうまく動かないので小細工をしてみた
おはようございます。
ちょこちょこ Vue を触っていて
とても便利なのですが、配列の監視がちょっとかゆいところに手が届かない感じですね。
配列を監視する際に一番知りたいのは
- 何行目のどの項目が変更されたのか
- 変更前、変更後の値
なんじゃないかと思うのですが、単純にできません。
また、watch 関数の引数には oldValue, newValue が渡されてくるのですが、どちらも同じものになっています。(ばぐ?仕様?)
ということで、
ひとまず何行目が変更されたのか、チェックできるようにしてみました。
プログラムは前回のものを流用。
【Vue.js】テーブル内にドロップダウンリストを設置してみる
スポンサーリンク
プログラム
CSS
css/style.css
@charset "UTF-8";
body
{
font-family:Helvetica, 'Helvetica Neue', 'Mplus 1p', 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Meiryo, メイリオ, Osaka, 'MS PGothic' !important;
font-size:12px;
}
h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6
{
font-family:Helvetica, 'Helvetica Neue', 'Mplus 1p', 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Meiryo, メイリオ, Osaka, 'MS PGothic' !important;
}
div#app .table-container{
height: 250px;
max-height: 250px;
overflow-y: auto;
margin: 10px 0px;
}
CSSに変更ありませんが、一応。
html
index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Vue テーブルにドロップダウン</title>
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.1/css/all.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body class="hold-transition fixed skin-blue-light sidebar-mini ">
<div class="wrapper" >
<div style="margin: 20px;">
<pre>
Vue.js で テーブル内にリストボックスを設置するサンプル
</pre>
</div>
<div class="container">
<div class="row">
<div class="col-xs-12">
<div id="app">
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>No</th>
<th>名前</th>
<th>年齢</th>
<th>性別</th>
<th>種類</th>
<th>好物</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in items">
<td><input type="text" v-model="item.no"></td>
<td><input type="text" v-model="item.name"></td>
<td><input type="text" v-model="item.age"></td>
<td><input type="text" v-model="item.sex"></td>
<td>
<select v-model="item.selectedKind" v-on:change="selectChange" >
<option v-for="option in item.kindList" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
</td>
<td><input v-model="item.favorite"></td>
</tr>
</tbody>
</table>
</div>
<div class="pull-right">
<button id="addButton" class="btn btn-success" v-on:click="add">追加</button>
<button id="modButton" class="btn btn-primary" v-on:click="mod">更新</button>
<button id="delButton" class="btn btn-danger" v-on:click="del">削除</button>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="js/script.js"></script>
</html>
各項目も変更できるようにテキストボックスにしました。
javascript
js/script.js
var _kindList = [
{value : '01', text: 'キジトラ'},
{value : '02', text: '長毛種'},
{value : '03', text: 'ミケ'},
{value : '04', text: 'サビ'},
];
var _items = [
{ no:'1', name:'そら', sex:'♂', age:'8', kind:'キジトラ', kindList: _kindList, selectedKind : "", favorite:'犬の人形'},
{ no:'2', name:'りく', sex:'♂', age:'7', kind:'長毛種', kindList: _kindList, selectedKind : "", favorite:'人間'},
{ no:'3', name:'うみ', sex:'♀', age:'6', kind:'ミケ', kindList: _kindList, selectedKind : "", favorite:'高級ウェットフード'},
{ no:'4', name:'こうめ', sex:'♀', age:'4', kind:'サビ', kindList: _kindList, selectedKind : "", favorite:'横取りフード'},
];
/**
* Vueインスタンス生成
*/
var app = new Vue({
el: '#app',
data:{
'items': _items,
'oldItems': []
},
watch: {
'items': {
handler: function(newValue, oldValue) {
oldValue = this.oldItems;
this.oldItems = JSON.parse(JSON.stringify(newValue));
if (oldValue.length < newValue.length) {
alert("行追加");
} else if (oldValue.length > newValue.length) {
alert("行削除");
} else {
for (let i = 0; i < newValue.length; i++) {
let oldJson = JSON.stringify(oldValue[i]);
let newJson = JSON.stringify(newValue[i]);
if (oldJson != newJson) {
alert((i+1) + "行目が変更されました");
}
}
}
},
deep: true
},
},
methods: {
add: function(event) {
console.log("addButton click");
let no = this.items.length + 1;
this.items.push({ no:no, name:'こなつ', sex:'♀', age:'8', kind:'キジトラ', kindList: _kindList, selectedKind : "", favorite:'布団' });
},
mod: function(event) {
console.log("modButton click");
this.items.find((item) => item.name === "こなつ").favorite = "高いところ";
},
del: function(event) {
console.log("dlelButton click");
this.items.splice(-1);
},
selectChange: function(event) {
console.log("Select Change");
},
},
created: function() {
console.log("created");
this.oldItems = JSON.parse(JSON.stringify(this.items));
}
})起動してみる
まとめ
変更前のデータを保持する変数を用意し、初期化時にデータをセットしてデータ変更時に更新していくという感じなのですが、
javascript は 単純に代入するだけだと、どちらの変数も同じものとして扱われるため、一度 Json(文字列)にパース、再度 Object にパースして代入してあげる必要があります。
(嚙み砕いてます)
そんでもって deep watch を定義して、新旧の比較を行えばどこが変更されたのかチェックができますね。
比較処理をもっと細かくすれば、どの項目が変更されたのかまでチェック可能です。
何かのお役に立てれば。
ではでは。
ディスカッション
コメント一覧
まだ、コメントがありません