目的
面白いAPIを見つけたのでせっかくだからWebアプリ作ってみよう
環境
Windows Subsystem for Linux
OS: Ubuntu
使用技術
Vue.js(SPAフレームワーク)
Vuetify(UIコンポーネントライブラリ)
axios(Ajax通信ライブラリ)
materialdesignicons(マテリアルデザインアイコン)
サーバーレス+小規模なアプリなのでいかに低コストで導入できるかで選びました
(ちょっとVue使ってみたかった感もある)
やったこと
プロジェクト作成
$ vue create animecheck
Vuetify追加
オプション指定などできるけどデフォルトを指定する
$ vue add vuetify
マテリアルデザインアイコン用のパッケージインストール
$ npm install @mdi/font --save
$ npm install material-design-iconfont --save
選択用コンボボックス作成
2014年~現在までの西暦とクールを選択する用のコンボボックスを作成する
<template src="../view/search.html">
</template>
<script src="../js/search.js">
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped src="../assets/css/search.css">
</style>
html, js, css それぞれ別々に書いたほうがわかりやすそうだったので、vueファイルはテンプレートのみ記載する
<v-layout row wrap>
<v-flex xs12 sm6>
<v-overflow-btn v-model="year" label='年' :items="selectYear" @change="getAnime"></v-overflow-btn>
</v-flex>
<v-flex xs12 sm6>
<v-overflow-btn v-model="season" label='クール(季節)' :items="selectSeason" @change="getAnime"></v-overflow-btn>
</v-flex>
</v-layout>
:items でコンボボックスに入れる選択値を設定する
選択された値は v-model で指定している year, season にそれぞれ入る
import axios from 'axios';
import 'vuetify/dist/vuetify.min.css'
import '@mdi/font/css/materialdesignicons.css'
import 'material-design-icons-iconfont/dist/material-design-icons.css'
export default {
data() {
return {
year: '',
season: '',
animes: [],
selectYear: this.getYear(),
selectSeason: [
{value: '1', text: '冬 (1 ~ 3 月)'},
{value: '2', text: '春 (4 ~ 6 月)'},
{value: '3', text: '夏 (7 ~ 9 月)'},
{value: '4', text: '秋 (10 ~ 12 月)'},
]
}
},
methods: {
/**
* 2014 ~ 現在までの年数を取得する関数
*/
getYear: function() {
let currentYear = (new Date()).getFullYear();
const baseYear = 2014;
let years = []
while(currentYear >= baseYear) {
years.push({value: currentYear, text: currentYear + '年'})
currentYear -= 1;
}
return years
},
}
...
}
クールだけは面倒なので固定で書いてしまった
(固定値として別の場所に書きたい)
アニメ取得APIとの連携
西暦とクールを選択したら、APIにリクエストを送りレスポンスをいい感じに加工して画面に表示する
<v-layout v-if="animes.length" row wrap>
<v-flex lg4 sm12 xs12 v-for="anime in animes">
<v-card raised tile class="grey lighten-3 ma-2">
<v-card-title primary-title>
<div>
<div class="headline">
{{ anime.title }}
</div>
</div>
</v-card-title>
<v-divider light></v-divider>
<v-card-actions class="pa-3">
<v-btn flat>
<a v-bind:href="anime.public_url">
<v-icon>
mdi-laptop
</v-icon>
</a>
</v-btn>
<v-btn flat>
<a v-bind:href="'https://twitter.com/' + anime.twitter_account">
<v-icon>
mdi-twitter
</v-icon>
</a>
</v-btn>
<v-btn flat>
<a v-bind:href="'https://twitter.com/search?q=%23' + anime.twitter_hash_tag + '&src=typd'">
<v-icon>
mdi-pound-box
</v-icon>
</a>
</v-btn>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
双方向データバインディングでanimesという変数に値が入っていない場合は、結果部分は何も表示されないようにする
/**
* 年 + クールから対象のアニメリストを取得する関数
*/
getAnime: function() {
this.getYear();
if (this.year && this.season) {
// API: ShangriLa Anime API V1
let api_url = "https://api.moemoe.tokyo/anime/v1/master/"
this.animes = []
axios.get(api_url + this.year + '/' + this.season, {
}).then(response => {
for (let i = 0; i < Object.keys(response.data).length; i++) {
this.animes.push(response.data[i])
}
})
}
}
コンボボックス側で指定されている関数
選択するたびに走るけど、年とクールが選択されていない限りは情報取得しないようにする
外部APIからjsonでデータ取得するためにはaxiosを使用する
アニメ情報取得には以下のAPIを使用しています
GET /anime/v1/master/:year/:n
:yearで指定されたYYYY年アニメの:nで指定されたクールの情報をすべて返します。
※参考
放映中のアニメ作品の情報を提供するAnime RESTful API サーバー作りました
axios を利用した API の使用
Vuetifyコンポーネントについて
Vuetify Component API Overview
公式のドキュメントに各コンポーネントの例とソースが記載されている
すごくわかりやすい
アイコンについて
Icon Component
Material Design Icons
Material Design Iconsのアイコンの利用をサポートしています。アイコン名にmdi-プレフィックスを付与するだけで使用できます。あなたのプロジェクトにMaterial Design Iconsを導入しておく必要があるので注意して下さい。
<v-icon>
mdi-twitter
</v-icon>
これでTwitterアイコンなどが使用できる
デプロイ
サーバーレス+SPAなので、Github Pagesで簡単に公開したい
そのためビルド先のフォルダをdistからdocsに変更する
プロジェクトフォルダ直下に以下のファイルを作成する
module.exports = {
publicPath: "./",
assetsDir: "",
outputDir: "docs"
}
ビルドする
$ npm run build
ビルド完了後にgitに上げてGithub Pagesの設定をしてあげれば出来上がり
完成品
とりあえずお手軽に作れました
最後に
こちらのAPIを使用させていただきました!素晴らしいAPIに感謝です!
https://github.com/Project-ShangriLa/sora-playframework-scala
Vueについて少ししか勉強してないのでもっと最適化できる部分あるかもって感じです
よろしければコメントなどでご指摘いただければと思います