商品一覧
検索結果{{count}}件
{{product.price | number_format}}円(税込)
送料無料
+送料{{product.delv | number_format}}円
データオプションにデータを定義していく
HTMLの中からユーザーの操作や商品の登録内容によって表示が変わる部分は全てデータ化する必要がある。
そこで、データ型(数値、文字列、真偽値、配列、オブジェクト)や変数名を考えてdataオプションに記述する。
HTMLの中からユーザーの操作や商品の登録内容によって表示が変わる部分は全てデータ化する必要がある。
そこで、データ型(数値、文字列、真偽値、配列、オブジェクト)や変数名を考えてdataオプションに記述する。
データの持たせ方
No. | 変数名 | データ型 | 説明 |
---|---|---|---|
1 | count | 数値 | 表示中の商品数 |
2 | showSaleitem | 真偽値 | セール対象・対象外の商品表示 |
3 | showDelvFree | 真偽値 | 送料無料・有料含むの商品表示 |
4 | sortOrder | 数値 | 初期表示・価格が安い順の表示 |
5 | products | 配列 | 商品リスト |
6 | name | 文字列 | 商品名 |
7 | price | 数値 | 商品価格(税込) |
8 | image | 文字列 | 商品画像のパス |
9 | delv | 数値 | 送料 |
10 | isSale | 真偽値 | セール対象・対象外 |
絞り込み機能の実装
●v-onディレクティブを使ってchangeイベントをハンドリングしてもよいが、データ更新検知のウォッチャを使えばテンプレート側にv-onを割り当てなくてもいいのでウォッチャを使う。➩productsから表示対象外のオブジェクトを削除すれば良さそうだが、再びチェック状態が変わってしまうと復元できなくなってしまう。
●配列要素を削除せずに算出プロパティで絞り込んだ結果を格納するための新しい配列を作成して、元の配列のproductsをループしながら1つ1つに条件を満たすか調べます。そして、テンプレートにバインドするプロパティをproductsからfilteredListに変更する。
●結果的にウォッチャは不要になる。理由は算出プロパティはリアクティブデータが更新されない限り、一度返した値はVue.jsによってキャッシュされ、2回目以降はキャッシュが参照される。よってチェック状態を切り替える度にキャッシュを破棄してfilteredListを再評価しfunction実行するので、ウォッチャがなくてもページの表示と連動します。
●v-onディレクティブを使ってchangeイベントをハンドリングしてもよいが、データ更新検知のウォッチャを使えばテンプレート側にv-onを割り当てなくてもいいのでウォッチャを使う。➩productsから表示対象外のオブジェクトを削除すれば良さそうだが、再びチェック状態が変わってしまうと復元できなくなってしまう。
●配列要素を削除せずに算出プロパティで絞り込んだ結果を格納するための新しい配列を作成して、元の配列のproductsをループしながら1つ1つに条件を満たすか調べます。そして、テンプレートにバインドするプロパティをproductsからfilteredListに変更する。
●結果的にウォッチャは不要になる。理由は算出プロパティはリアクティブデータが更新されない限り、一度返した値はVue.jsによってキャッシュされ、2回目以降はキャッシュが参照される。よってチェック状態を切り替える度にキャッシュを破棄してfilteredListを再評価しfunction実行するので、ウォッチャがなくてもページの表示と連動します。
並び替え機能を実装する
filteredListの算出処理の中で、セレクトボックスにバインドしたsortOrderの値を読み取り、並び替えた結果を返すようにしていきます。
この方法ならfilteredListは常に絞り込みと並び替えの両方を反映した配列を返すことになります。
filteredListの算出処理の中で、セレクトボックスにバインドしたsortOrderの値を読み取り、並び替えた結果を返すようにしていきます。
この方法ならfilteredListは常に絞り込みと並び替えの両方を反映した配列を返すことになります。
商品にkeyを指定する
表示を切り替える要素にはvue.jsが一つ一つの要素を区別できるようにkey属性を指定します。商品番号やIDなどの重複の無い値を設定する。
表示を切り替える要素にはvue.jsが一つ一つの要素を区別できるようにkey属性を指定します。商品番号やIDなどの重複の無い値を設定する。
「this」はクラスのインスタンスが生成されたときに、そのインスタンスを指すキーワードです。記述場所のスコープによって何を指すかが変わってきます。
HTML
<div id="app">
<div class="container">
<h1 class="pageTitle">商品一覧</h1>
<!--検索欄-->
<div class="search">
<div class="result">
検索結果<span class="count">{{count}}件</span>
</div>
<div class="condition">
<div class="terget">
<label><input type="checkbox" v-model="showSaleItem">セール商品</label>
<label><input type="checkbox" v-model="showDelvFree">送料無料</label>
</div>
<div class="sort">
<!--文字列型になる数値を.number修飾子で数値型へ-->
<label for="sort">並び替え</label>
<select id="sort" class="sorting" v-model.number="sortOrder">
<option value="1">標準</option>
<option value="2">価格が安い順</option>
</select>
</div>
</div>
</div>
<!--商品一覧-->
<div class="list">
<div class="item" v-for="product in filteredList" v-bind:key="products.id">
<figure class="image">
<template v-if="product.isSale">
<div class="status">SALE</div>
</template>
<img v-bind:src="product.image" alt="">
<!-- <figcaption>{{product.name}}</figcaption> -->
<figcaption v-html="product.name"></figcaption>
</figure>
<div class="detail">
<div class="price"><span>{{product.price | number_format}}</span>円(税込)</div>
<template v-if="product.delv == 0">
<div class="shipping-fee none">送料無料</div>
</template>
<template v-else>
<div class="shipping-fee none">+送料{{product.delv | number_format}}円</div>
</template>
</div>
</div>
</div>
</div>
</div><!-- app -->
JavaScript
Vue.filter('number_format', function (val) {
return val.toLocaleString();
});
//商品一覧コンポーネント
var app = new Vue({
el: '#app',
data: {
showSaleItem: false, //SALE商品のチェック状態Vue.filter
showDelvFree: false, //送料無料のチェック状態
sortOrder: 1, //並び替えの選択値(1:標準 2:価格が安い順)
products: [ //商品リスト
{ id: 1, name: 'ジム<br>クライミング体験会', price: 1550, image: '../images/clim.jpg', delv: 0, isSale: true },
{ id: 2, name: 'ジム<br>クライミング観戦チケット', price: 1280, image: '../images/clim.jpg', delv: 0, isSale: true },
{ id: 3, name: '福岡会場<br>公式戦観戦チケット', price: 1680, image: '../images/clim.jpg', delv: 190, isSale: true },
{ id: 4, name: '外岩講習会<br>外岩クライミング体験チケット', price: 500, image: '../images/clim.jpg', delv: 0, isSale: true },
{ id: 5, name: 'リード<br>リードクライミング講習', price: 890, image: '../images/clim.jpg', delv: 0, isSale: false },
{ id: 6, name: 'ロープの使い方<br>クライミング座談会', price: 990, image: '../images/clim.jpg', delv: 0, isSale: false }
]
},
computed: {
//絞り込み後の商品リストを返す算出プロパティ
filteredList: function () {
var newList = [];
for (var i = 0; i < this.products.length; i++) {
var isShow = true;
//i番目の商品が表示対象かどうかを判定する
if (this.showSaleItem && !this.products[i].isSale) {
isShow = false;
}
if (this.showDelvFree && this.products[i].delv > 0) {
isShow = false;
}
if (isShow) {
newList.push(this.products[i]);
}
}
//新しい配列を並び替える
if (this.sortOrder == 1) {
//元の順番にpushしているので並び替え済み
}
else if (this.sortOrder == 2) {
newList.sort(function (a, b) {
return a.price - b.price;
});
}
return newList;
},
//絞り込み後の商品件数を返す算出プロパティ
count: function () {
return this.filteredList.length;
}
}
});