各コンポーネントに保持するデータ

コンポーネント データ 定義場所 備考
ルート(lineUp.js) 商品データ dataオプション 実際のアプリケーションでは外部から受けとる
商品一覧(product-list.js) 商品データ propsオプション 親コンポーネント(lineUp.js)から受け取る
検索条件 dataオプション 子コンポーネント(product-header.js)に渡す
ヘッダー(product-header.js) 検索条件 propsオプション 親コンポーネント(product-list.js)から受け取る
表示件数 propsオプション 親コンポーネント(product-list.js)から受け取る
商品(product.js) 商品データ propsオプション 親コンポーネント(product-list.js)から受け取る
※子は親のテンプレート内で使われるので、親より先に読み込まないとVue.jsが解析失敗してエラーになります。

ルートコンポネントのテンプレート

<div id="app">
<product-list v-bind:products="products"></product-list>
</div>
・・・・
・・・・
↓↓
<script src="../common/filter.js"></script>
<script src="components/product-header.js"></script>
<script src="components/product.js"></script>
<script src="components/product-list.js"></script>
<script src="../scripts/lineUp.js"></script>
</body>

ルートコンポネントのスクリプト

var app = new Vue({
  el: '#app',
  data: {
    //商品リスト
    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 }
    ]
  }
});

フィルターのスクリプト

//通貨書式に変換するフィルター
Vue.filter('number_format', function (val) {
  return val.toLocaleString();
});

商品のコンポーネント

var product = {
  template: `
    <div class="item">
      <figure class="image">
        <template v-if="product.isSale">
          <div class="status">SALE</div>
        </template>
        <img v-bind:src="product.image" alt="">
        <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">+送料{{ product.delv | number_format }}円</div>
        </template>
      </div>
    </div>`,
  props: ['product']
};

ヘッダーのコンポーネント

var productHeader = {
  template: `
    <header>
      <h1 class="pageTitle">商品一覧</h1>
      <!--検索欄-->
      <div class="search">
        <div class="result">
          検索結果<span class="count">{{ count }}件</span>
        </div>
        <div class="condition">
          <div class="target">
            <label>
              <input type="checkbox"
                v-bind:checked="showSaleItem"
                v-on:change="$emit('showSaleItemChanged')">セール対象
            </label>
            <label>
              <input type="checkbox"
                v-bind:checked="showDelvFree"
                v-on:change="$emit('showDelvFreeChanged')">送料無料
            </label>
          </div>
          <div class="sort">
            <label for="sort">並び替え</label>
            <select id="sort" class="sorting"
              v-bind:value="sortOrder"
              v-on:change="$emit('sortOrderChanged', parseInt($event.target.value))">
              <option value="1">標準</option>
              <option value="2">価格が安い順</option>
            </select>
          </div>
        </div>
      </div>
    </header>`,
  props: ['count', 'showSaleItem', 'showDelvFree', 'sortOrder']
};

商品一覧コンポーネント

Vue.component('product-list', {
  template: `
    <div class="container">
      <product-header
        v-bind:count="filteredList.length"
        v-bind:showSaleItem="showSaleItem"
        v-bind:showDelvFree="showDelvFree"
        v-bind:sortOrder="sortOrder"
        v-on:showSaleItemChanged="showSaleItem=!showSaleItem"
        v-on:showDelvFreeChanged="showDelvFree=!showDelvFree"
        v-on:sortOrderChanged="sortOrderChanged">
      </product-header>
      <div class="br200"></div>
      <div class="list">
        <product
          v-for="product in filteredList"
          v-bind:product="product"
          v-bind:key="product.id">
        </product>
      </div>
    </div>`,
  components: {
    'product-header': productHeader,
    'product': product
  },
  props: ['products'],
  data: function () {
    return {
      showSaleItem: false,
      showDelvFree: false,
      sortOrder: 1
    }
  },
  methods: {
    //並び替えの選択が変わったときに呼び出されるメソッド
    sortOrderChanged: function (order) {
      this.sortOrder = order;
    }
  },
  computed: {
    filteredList: function () {
      var newList = [];
      for (var i = 0; i < this.products.length; i++) {
        var isShow = true;
        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;
    }
  }
});