Vueでいい感じにしたいという動機
3年ほど前
とある業務の中で、JSON形式で取得したリストをjsでぶん回して表を生成することがありまして、
ただ、いい感じにソートとかできたら嬉しいという要望があり、
きっとこれはいちいち画面遷移するような挙動だと実現できない&jQueryでDOMを書き換えするのも重くなりすぎる、、、
あ、そうだ仮想DOMってやつをやってみようと思って実装した備忘録です。
(実際に業務で使用したものを当たり障りない形に修正しています。)
実際の開発環境は以下
- jQuery1.xx
- BootStrap 3.xx
HTML
画面内ソートするためのinputタグなんかをベタで書いてます。これは参考用
table内のtbodyをv-forで回してリストを生成しています。(これは便利)
<div id="list" v-cloak>
<div>
<div>
<div>
<h4>人数(<span>{{sortedList.length}}</span>件)</h4>
</div>
<div>
<div>
<table class="table table-bordered">
<tr>
<th>管理番号</th>
<td><input type="text" v-model="search01"></td>
<td>
<select v-model="sort01">
<option value=""></option>
<option value="asc">昇順</option>
<option value="desc">降順</option>
</select>
</td>
<th>氏名</th>
<td><input type="text" v-model="search02"></td>
<th>氏名カナ</th>
<td><input type="text" v-model="search03"></td>
</tr>
<tr>
<th>性別</th>
<td>
<select v-model="search04">
<option value=""></option>
<option value="男性">男性</option>
<option value="女性">女性</option>
<option value="その他">その他</option>
</select>
</td>
<th>住所</th>
<td><input type="text" v-model="search05"></td>
<th>出身校</th>
<td><input type="text" v-model="search06"></td>
</tr>
<tr>
<th>趣味</th>
<td><input type="text" v-model="search07"></td>
<th>配偶者</th>
<td><input type="text" v-model="search08"></td>
<th>子供</th>
<td><input type="text" v-model="search09"></td>
</tr>
<th>住まい</th>
<td><input type="text" v-model="search10"></td>
<td colspan="4"></td>
</tr>
</table>
</div>
</div>
</div>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>写真</th>
<th>管理番号<br/>フリガナ<br/>名前</th>
<th>性別</th>
<th>住所</th>
<th>出身校</th>
<th>趣味</th>
<th>配偶者</th>
<th>子供</th>
<th>住まい</th>
</tr>
</thead>
<tbody>
<tr v-for="person in sortedList">
<td><img v-lazy="person.photo" v-bind:alt="person.name" class="lazy" width="45" /></td>
<td>{{person.id}}<br/>{{person.name_kana}}<br/>{{person.name}}</td>
<td>{{person.gender}}</td>
<td>{{person.address}}</td>
<td>{{person.shcool}}</td>
<td>{{person.hobby}}</td>
<td>{{person.spouse}}</td>
<td>{{person.child}}</td>
<td>{{person.Housing}}</td>
</tr>
</tbody>
</table>
・・・
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
DATA
適当なJSON(適当です。名前も適当)
var membersList = [
{photo:"yamada.png",id:"0001",name:"山田太郎",name_kana:"ヤマダタロウ",gender:"男",address:"東京",shcool:"東京大学",hobby:"旅行",spouse:"いる",child:"なし",Housing:"賃貸マンション"},
{photo:"suzuki.png",id:"0002",name:"鈴木次郎",name_kana:"スズキジロウ",gender:"男",address:"神奈川",shcool:"横浜大学",hobby:"写真",spouse:"なし",child:"なし",Housing:"持ち家"},
{photo:"kobayashi.png",id:"0003",name:"小林花子",name_kana:"コバヤシハナコ",gender:"女",address:"京都",shcool:"立命館大学",hobby:"座禅",spouse:"いる",child:"いる",Housing:"持ち家"}
・・・];
SCRIPT
画面読み込み中に写真が読み込まれるのを待ってると、良さが損なわれる気がしたので、レイジーロード探して、実装しました。
Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dummy.png',
loading: 'dummy.png',
attempt: 1
});
当たり障りない感じに変えていますが、
実装の記述に近い形で書いています。もう少しスマートにできないものか、、、
var app1 = new Vue({
el:'#list'
,data:{
search01:''
,search02:''
,search03:''
,search04:''
,search05:''
,search06:''
,search07:''
,search08:''
,search09:''
,search10:''
,sort01:''
,members:membersList
}
,computed:{
filteredList01: function(){ //管理番号
return this.members.filter(function(item){
return item.id.indexOf(this.search01) > -1
}, this)
}
,filteredList02: function(){ //氏名
return this.filteredList01.filter(function(item){
return item.name.indexOf(this.search02) > -1
}, this)
}
,filteredList03: function(){ //氏名カナ
return this.filteredList02.filter(function(item){
return item.name_kana.indexOf(this.search03) > -1
}, this)
}
,filteredList04: function(){ //性別
return this.filteredList03.filter(function(item){
return item.gender.indexOf(this.search04) > -1
}, this)
}
,filteredList05: function(){ //住所
return this.filteredList04.filter(function(item){
return item.address.indexOf(this.search05) > -1
}, this)
}
,filteredList06: function(){ //出身校
return this.filteredList05.filter(function(item){
return item.shcool.indexOf(this.search06) > -1
}, this)
}
,filteredList07: function(){ //趣味
return this.filteredList06.filter(function(item){
return item.hobby.indexOf(this.search07) > -1
}, this)
}
,filteredList08: function(){ //配偶者
return this.filteredList07.filter(function(item){
return item.spouse.indexOf(this.search08) > -1
}, this)
}
,filteredList09: function(){ //子供
return this.filteredList08.filter(function(item){
return item.child.indexOf(this.search09) > -1
}, this)
}
,filteredList10: function(){ //住まい
return this.filteredList09.filter(function(item){
return item.Housing.indexOf(this.search10) > -1
}, this)
}
,sortedList: function(){ //ソート後のデータを返す算出プロパティ sortedList
var copy = this.filteredList10.slice()
if(this.sort01 === 'asc' ){
//comparatorAsc01 メソッドをコールバックとして sort 関数に渡す
return copy.sort(this.comparatorAsc01)
}else if(this.sort01 === 'desc') {
//降順に並べ替えるためのコールバック関数は comparatorDesc02
return copy.sort(this.comparatorDesc02)
}else{
return copy
}
}
}
,methods:{
comparatorAsc01: function(itemA, itemB){ //昇順
if(itemA.id < itemB.id){
return -1
} else if(itemA > itemB.id){
return 1
} else{
return 0
}
}
,comparatorDesc02: function(itemA, itemB){ //降順
if(itemA.id > itemB.id){
return -1
} else if(itemA < id){
return 1
} else{
return 0
}
}
}
});
結果
記述は改善の余地があると思いますが、
機能としては満足いただいていたかと思います。
本来、選んだあと画面が再リロードして検索結果が表示されることしかできなかったものが
選んだ瞬間に表が入れ替わる様は思わず「おお」となりました。
無事にIEでも動きましたとさ。。。よかった。
まだまだ世の中、レガシーな環境はあるということで、
少しずつ良くしていければいいなと思います。
おわり