前回、Vue.jsのトリコになって、試しに時計アプリを作成してみましたが、二回目の今回はもう少し複雑なアプリを作りたいと思います。
作るのはiTunesのAPIを使ったiTunesSearchです!
実装ポイントはAPIとの通信による非同期処理とそれに伴うローディングの実装、親コンポーネントと子コンポーネントの連携方法などです。
基本的な開発環境の構築は前回の記事を参考にしてください。
今回は axios というhttp通信を行う為のライブラリを使用するので、
コマンドプロンプト(ターミナル)に
npm install -S axios
と、入力してインストールしてください。
これで準備は完了です。
前回同様、ファイルの整理から始めます。
を開いて
<template> </template> <script> </script> <style> </style>
一旦、空にします。
続いて
を削除します。
最初に検索フォームをつくるため、
を作成します。
まずは、script部分からです。
<script>
import axios from "axios";
export default {
data() {
return {
term: "",
}
},
methods: {
async exe() {
this.$emit("loadStart")
const { data } = await axios.get(`//itunes.apple.com/search?term=${this.term}&country=jp&entity=musicVideo`);
this.$emit("loadComplete", { results: data.results })
},
},
};
</script>
続いて、template部分です。
<template> <div> <div class="container"> <input class="text" type="text" v-model="term" @keyup.enter="exe"> <input class="submit" type="submit" value="Search" @click="exe"> </div> </div> </template>
また、装飾子も用意されていて
他にも色々用意されているので公式サイトでご確認ください。
最後に、style部分です。
<style scoped>
.container {
display: flex;
justify-content: center;
height: 70px;
padding: 20px;
background-color: #35495e;
box-sizing: border-box;
}
.text {
width: 50%;
max-width: 300px;
padding: .5em;
border: none;
}
.submit {
padding: .5em 2em;
margin-left: 10px;
color: #fff;
background-color: #42b883;
border: none;
border-radius: 20px;
}
</style>
続いてローディング画面のために
を作成します。
ここはCSSアニメーションでつくるので、templateとstyleだけです。
template部分
<template> <div> <div class="item"></div> </div> </template>
style部分
<style scoped>
.item {
width: 50px;
height: 50px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
border: 3px solid #42b883;
border-top-color: transparent;
border-radius: 50%;
animation: spin 0.75s infinite linear;
}
.item::after {
content: "";
position: absolute;
top: -3px;
left: -3px;
width: inherit;
height: inherit;
border: inherit;
border-radius: inherit;
transform: rotate(60deg);
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>
続いて検索結果を表示する
を作成します。
まずは、script部分です。
<script>
import Loading from "@/components/Loading";
export default {
props: ["items", "loadProgress"],
components: {
Loading,
},
methods: {
getYear(dateStr) {
const date = new Date(dateStr)
return date.getFullYear()
},
},
};
</script>
続いて、template部分です。
<template>
<div>
<ul class="list">
<li class="item" v-for="item of items" :key="item.trackId">
<div class="item-inner">
<div class="photo">
<img class="photo-img" :src="item.artworkUrl100" :alt=item.trackName>
</div>
<div class="content">
<p><a class="track" :href="item.trackViewUrl" target="_blank">{{ item.trackName }}</a></p>
<p><a class="artist" :href="item.artistViewUrl" target="_blank">{{ item.artistName }}</a></p>
<div class="data"> {{ getYear(item.releaseDate) }} / {{ item.primaryGenreName }} / ¥{{ item.trackPrice }}</div>
</div>
</div>
</li>
</ul>
<Loading class="loading" v-show="loadProgress"/>
</div>
</template>
v-for
反復処理を行います。
ここで言うと、親コンポーネントから送られてきたitemsの中身を繰り返しています。
また、key属性に一意な値を設定することが推奨されています。(:を使って動的に値を設定します)
v-show
値の真偽に応じて表示・非表示を切り替えるものです。
ここでは、loadProgressの値に応じてLoadingコンポーネントの表示・非表示を切り替えています。
最後にstyleです。
<style scoped>
.item {
padding: 20px 0;
}
.item:nth-of-type(even) {
background-color: #f5f5f5;
}
.item-inner {
display: flex;
width: 90%;
max-width: 600px;
margin: auto;
}
.photo {
flex: 0 0 150px;
}
.photo-img {
width: 100%;
display: block;
}
.content {
flex: 1 1;
padding-left: 20px;
}
.track {
color: #42b883;
font-size: 2rem;
font-weight: 700;
text-decoration: none;
}
.artist {
display: block;
color: #42b883;
font-size: 1.4rem;
font-weight: 700;
text-decoration: none;
}
.data {
margin-top: 1.5em;
text-align: right;
font-size: 1.2rem;
}
.loading {
position: fixed;
top: 70px;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
background: #35495e;
}
</style>
最後に今までにつくったコンポーネントを画面に表示させるために
を編集していきます。
まずは、scriptです。
<script>
import Search from "@/components/Search";
import Result from "@/components/Result";
export default {
data() {
return {
items: [],
loadProgress: false,
};
},
methods: {
onLoadStart() {
this.loadProgress = true;
},
onLoadComplete({ results }) {
this.items = results;
this.loadProgress = false;
},
},
components: {
Search,
Result,
},
};
</script>
続いてtemplateです。
<template> <div class="root"> <Search class="search" @loadStart="onLoadStart" @loadComplete="onLoadComplete"/> <Result :items="items" :loadProgress="loadProgress"/> </div> </template>
最後にstyleです。
<style scoped>
.root {
padding-top: 70px;
}
.search {
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1;
}
</style>
<style>
html {
font-size: 62.5%;
}
body {
margin: 0;
color: #35495e;
}
p {
margin: 0;
}
ul {
padding: 0;
margin: 0;
}
</style>
前回同様、ページ全体に適用させるstyleも定義します。
これで↓のような画面が表示されるはずです。

テキストエリアに何か入力してEnterキーを押すか、Searchボタンをクリックしてください。

このように検索結果が表示されれば成功です!
前回と比べて少し複雑だったと思いますが、素のJavaScriptで作るより断然簡単だったのではないでしょうか?
次回は Vue Router を使ったルーティング機能をご紹介したいと思います!