Vue.js+Vuetifyでロードバイクのブランド辞典を作る②　～実装編～

#はじめに
この記事は[「Vue.js+Vuetifyでロードバイクのブランド辞典を作る①　～Vuetify導入編～」](https://qiita.com/takasynapse/items/d7762691565095fc0b61)の続きとなっております。
前回の記事では、VueプロジェクトにVuetifyを導入して、公式ドキュメントのカードコンポーネントからよさげなサンプルを見つけてくるところまでやりました。今回は、その続きからやっていきたいと思います。
先に全体のソースコードが見たいという方は、「Listコンポーネントを作成して効率よくコードを書く」の項目へ飛んでください。

#カードを作る
では、前回見つけたサンプル2つを組み合わせて1枚のカードを作る所から始めましょう。
<table>
<tr>
<td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513585/41b2373d-ce76-65eb-eca4-8f4e1deffe61.png" >
<td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513585/8ed9361a-85a2-74cf-2bbe-dbceb9d03849.png">
<tr>
</table>

左のカードからは
・EXPLOREを押すと、隠れていたテキストが表示される機能
右のカードからは
・画像にテキストを重ねるデザイン
・SAHRE、EXPLOREのボタン（今回はこれを公式サイトへのリンクにする）
を採用しました。
結果、こうなりました。
見やすいように、ブランド名のところに背景色を設定しています。
<img width="350px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513585/aa2ba41e-6ff3-f71c-b959-bc112f2bb377.png">
template部分のソースコードはこちらになります。
自転車の画像やブランド名といった情報はscriptのdataに格納しデータバインディングしています。（今はわかりやすさのために日本語で書いています）

```Vue
<v-card
  class="mx-auto"
  max-width="350"
>
  <v-img
    class="white--text align-end"
    height="200px"
    src=ロードバイクの画像URL
  >
    <!-- ブランド名の背景色を灰色に設定 -->
    <v-card-title style="background-color: rgba(125, 125, 125, 0.6); padding: 10px 20px;">{{ ブランド名 }}</v-card-title>
  </v-img>
  <v-card-subtitle class="pb-0">
    {{ モデル名 }}<br>
    {{ 価格 }}
  </v-card-subtitle>
  <v-card-actions>
    <v-btn
      color="orange lighten-2"
      text
    >
      Explore
    </v-btn>                 
    <v-spacer></v-spacer>
    <v-btn
      icon
      @click="show = !show"
    >
      <v-icon>{{ show ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
    </v-btn>
  </v-card-actions>
  <v-expand-transition>
    <div v-show="show">
      <v-divider></v-divider>
      <v-card-text class="text--primary">
        {{ ブランドの説明 }}
      </v-card-text>
    </div>
  </v-expand-transition>
  <v-card-actions>
    <a href=公式サイトへのリンク style="text-decoration: none;">
        <v-btn
          color="orange"
          text
        >
          公式サイトへ
        </v-btn>
    </a>
  </v-card-actions>
</v-card>
```

#リストレンダリングで国別に描画する
![スクリーンショット (229).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513585/45f1e1f2-4e97-468d-5185-ecfbcfd7ecaa.png)

さて、前項でカードの見た目は完成しました。今度はこれを[リストレンダリング](https://jp.vuejs.org/v2/guide/list.html)を用いて国ごとにデータを描画していきましょう。今回は例として、アメリカに本社を置くブランドを集めた"USbikes"という配列にデータを格納していきたいと思います。
配列に格納するデータは以下の通りです。
・国名(英語)
・国名(日本語)
・ロードバイクの画像URL
・ブランド名
・フラグシップモデル名
・フラグシップモデルの価格
・ブランドの説明（公式サイトから適当に引っ張ってきました）
・公式サイトのURL
・EXPLOREが開かれているかどうかを判定するフラグ

リストレンダリングするには、v-forディレクティブを使用して"bike in USbikes"の形式で要素を描画していけばいいですね。v-imgとv-btnを囲むaタグにはv-bindを用いてURLを動的に渡している点には注意してください。（ただし、今回は":"を用いた省略記法で書いています）
v-rowやv-colといったVuetifyのグリッドシステムについては公式ドキュメントをご参照ください。→[Vuetify公式ドキュメントGridSystem](https://vuetifyjs.com/ja/components/grids/)


```Vue:App.vue
<template>
  <v-container>
    <div class="country">
      <hr />
      <h1>{{ this.USbikes[0].country }}</h1>
      <p>{{ this.USbikes[0].countryJa }}</p>
    </div>
    <v-row justify="start">
      <v-col v-for="bike in USbikes" :key="bike.brand" md="4">
        <v-card class="mx-auto" max-width="350">
          <v-img class="white--text align-end" height="200px" :src="bike.img">
            <v-card-title
              style="
                background-color: rgba(125, 125, 125, 0.6);
                padding: 10px 20px;
              "
              >{{ bike.name }}</v-card-title
            >
          </v-img>

          <v-card-subtitle class="pb-0">
            {{ bike.model }}<br />
            {{ bike.price }}
          </v-card-subtitle>
          <v-card-actions>
            <v-btn color="orange lighten-2" text> Explore </v-btn>
            <v-spacer></v-spacer>

            <v-btn icon @click="bike.show = !bike.show">
              <v-icon>{{
                bike.show ? "mdi-chevron-up" : "mdi-chevron-down"
              }}</v-icon>
            </v-btn>
          </v-card-actions>

          <v-expand-transition>
            <div v-show="bike.show">
              <v-divider></v-divider>
              <v-card-text class="text--primary">
                {{ bike.example }}
              </v-card-text>
            </div>
          </v-expand-transition>

          <v-card-actions>
            <a :href="bike.url" style="text-decoration: none">
              <v-btn color="orange" text> 公式サイトへ </v-btn>
            </a>
          </v-card-actions>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
    name: "App",
    data: ()=> ({
        USbikes:[
            {
                country:'USA',
                countryJa:'アメリカ',
                img: 'https://embed.widencdn.net/img/dorelrl/b3aolw3spj/2000px@1x/C21_C11251M_SuperSix_EVO_HM_LAV_PD.png',
                name:'cannondale', 
                model:'SuperSix EVO Hi-MOD Disc Red eTap AXS',
                price:'¥1,155,000',
                example:'cannondaleは本社をアメリカに置くバイクブランドです。',
                url:'https://www.cannondale.com/ja-jp',
                show: false,
            },
            {
                country:'USA',
                countryJa:'アメリカ',
                img:'https://s7d5.scene7.com/is/image/Specialized/?layer=0&wid=1920&hei=640&fmt=jpg&src=is{Specialized/pdp-product-bg-dark?wid=1920&hei=640}&layer=1&src=is{Specialized/90620-05_TARMAC-SL7-SW-DI2-SAGAN-COLL-DECON-GRN-YEL_HERO?wid=920&hei=600&$hybris-pdp-hero$}',
                name:'SPECIALIZED',
                model:'S-Works Tarmac SL7 - Sagan Collection',
                price:'¥1,507,000',
                example:'スペシャライズドは先進技術を駆使したサイクリング機材を提案し、トップレーサーから情熱的なホビー・サイクリストまで、ライダーの生活を向上し、革新するアメリカの自転車ブランドです。',
                url:'https://www.specialized.com/jp/ja',
                show: false,
            },
            {
                country:'USA',
                countryJa:'アメリカ',
                img:'https://trek.scene7.com/is/image/TrekBicycleProducts/EmondaSLR9Disc_21_33141_A_Primary?$responsive-pjpg$&cache=on,on&wid=1920&hei=1440',
                name:'TREK',
                model:'Émonda SLR 9',
                price:'¥1,316,700',
                example:'TREKです',
                url:'https://www.trekbikes.com/jp/ja_JP/',
                show: false,
            }
        ]
    })
};
</script>

<style scoped>
/*国名の英語と日本語表記を横並びにするためにdisplay:inline-block;を指定*/
.country h1 {
  display: inline-block;
}
.country p {
  display: inline-block;
}

hr {
  margin: 10px 0;
}
</style>
```

#Listコンポーネントを作成して効率よくコードを書く
前項でアメリカブランドについては一覧を作ることが出来ました。他の国のブランドについても同様に作成していけばよいのですが、イチイチ国ごとにtemplateを書いていてはコード量が膨大になり大変です。なので、リストレンダリングを行うListコンポーネントを作成し、それを再利用して効率よくコードを書いていきましょう。イメージとしてはこんな感じです。![スクリーンショット (231).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513585/1c3ba19f-e8ff-3134-b21c-9f6051afa32e.png)
App.vueに国ごとに配列を作成し、そのデータを子コンポーネントであるList.vueに渡してあげます。親コンポーネントから子コンポーネントにデータを渡す方法を軽くおさらいしましょう。
1.子コンポーネントを親コンポーネントにimportする。
2.親コンポーネントのcomponents:{}に子コンポーネントを指定する。
3.子コンポーネントのタグを用意して、送りたいdataをv-bindで指定する。
4．子コンポーネントのpropsに受け取り口を作成する。
5.子コンポーネントでdataのように使用する。
です。
詳しくは[公式ドキュメント](https://jp.vuejs.org/v2/guide/components.html#%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%9F%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%B8%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E5%8F%97%E3%81%91%E6%B8%A1%E3%81%97)をご参照ください。

そのようにして今回は、App.vueにJPbikes,TWbikes...といったように国ごとに配列を作成し、Listコンポーネントにv-bindを用いて動的にデータを渡しました。そしてListコンポーネントでそれを受け取り、thisを用いてそのデータにアクセスして描画しています。簡略化のために、下のコードではブランドの情報は省略しています。

```Vue:App.vue
<template>
  <v-app>
    <v-main>
      <div class="contents">
            <List :bikes="this.JPbikes"></List>
            <List :bikes="this.TWbikes"></List>
            <List :bikes="this.USbikes"></List>
            <List :bikes="this.CAbikes"></List>
            <List :bikes="this.UKbikes"></List>
            <List :bikes="this.ITAbikes"></List>
            <List :bikes="this.FRbikes"></List>
            <List :bikes="this.ESbikes"></List>
            <List :bikes="this.BELbikes"></List>
            <List :bikes="this.SWbikes"></List>
            <List :bikes="this.GEbikes"></List>
            <List :bikes="this.NZbikes"></List>
      </div>
    </v-main>
  </v-app>
</template>

<script>
import List from './components/List';

export default {
  name: 'App',
  components: {
    List,
  },
  data: () => ({
    JPbikes:[],
    TWbikes:[],
    USbikes:[],
    CAbikes:[],
    UKbikes:[],
    ITAbikes:[],
    FRbikes:[],
    ESbikes:[],
    BELbikes:[],
    SWbikes:[]
    GEbikes:[],  
    NZbikes:[],
  })
};
</script>

<style>
.contents{
  background-color: rgb(250, 250, 250);
}
h1{
  margin-top: 50px;
  margin-left: 10px;
  font-size: 45px;
  font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif
}
p{
  font-size: 15px;
  margin-left: 10px;
  color: rgb(107, 107, 107);
}
</style>
```

```Vue:List.vue
<template>
  <v-container>
    <hr>
    <div class="country">
      <h1>{{ this.bikes[0].country }}</h1>
      <p>{{ this.bikes[0].countryJa }}</p>
    </div>
    <v-row justify="start">
      <v-col v-for="bike in bikes" :key="bike.title" md="4">
        <v-card class="mx-auto" max-width="350">
          <v-img class="white--text align-end" height="200px" :src="bike.img">
            <v-card-title
              style="
                background-color: rgba(125, 125, 125, 0.6);
                padding: 10px 20px;
              "
              >{{ bike.name }}</v-card-title
            >
          </v-img>

          <v-card-subtitle class="pb-0">
            {{ bike.model }}<br />
            {{ bike.price }}
          </v-card-subtitle>
          <v-card-actions>
            <v-btn color="orange lighten-2" text> Explore </v-btn>
            <v-spacer></v-spacer>

            <v-btn icon @click="bike.show = !bike.show">
              <v-icon>{{
                bike.show ? "mdi-chevron-up" : "mdi-chevron-down"
              }}</v-icon>
            </v-btn>
          </v-card-actions>

          <v-expand-transition>
            <div v-show="bike.show">
              <v-divider></v-divider>
              <v-card-text class="text--primary">
                {{ bike.example }}
              </v-card-text>
            </div>
          </v-expand-transition>

          <v-card-actions>
            <a :href="bike.url" style="text-decoration: none">
              <v-btn color="orange" text> 公式サイトへ </v-btn>
            </a>
          </v-card-actions>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "List",
  props: ["bikes"],
};
</script>

<style scoped>
.country h1 {
  display: inline-block;
}
.country p {
  display: inline-block;
}

hr {
  margin: 10px 0;
}
</style>
```

これでサイトは完成です。

#おわりに
「Vue.js+Vuetifyでロードバイク辞典を作る」をここまで読んでいただきありがとうございました。
私もこのサイトの作成を通じて初めてVuetifyに触れたので、もし間違っている点などがございましたらコメントで指摘していただけると幸いです。