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