前回、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 を使ったルーティング機能をご紹介したいと思います!