【TypeScript&Nuxt.js】占いアプリ⑤応用：占いAPIを取り入れてみる-1

### 前回はこちら
https://qiita.com/sakuc47/items/af3002209f9ea652fe4a

### 最初の記事はこちら
https://qiita.com/sakuc47/items/60f7a3d3b9500059030e

## 進め方
1.	タロットカードのデータをTypeScriptで配列にする ✅
2.	その中からランダムに1枚選ぶロジックを書く ✅
3.	結果を表示する画面を作る（1ページ）✅
4.	カードごとに画像を表示してみる	
5.	NuxtLinkでページ遷移（トップ→結果ページ）
6.	デザインを整える
7.	**応用：今日の運勢APIを取り入れてみる** 👈
8.	データの組み合わせで文章を作る

今日は占いAPIを取り入れてみようと思います🔮


## 今回使うAPIについて
非商用であれば無料で占い結果を提供してもらえるAPIです。
リンクの記載必須などのい利用の条件がいくつかあるので注意し、商用利用する場合は有料版を使うようにしてください。

https://jugemkey.jp/api/waf/api_free.php

:::note info
- リクエストした日の占い結果が取得できる
- 取得できる内容は以下の通り
    - 占いの内容
    - 金運（5段階評価）
    - 仕事運（5段階評価）
    - 恋愛運（5段階評価）
    - 総合運（5段階評価）
    - ラッキーアイテム
    - ラッキーカラー
    - ランキング
    - 星座名
:::

このAPIを使って、星座占いもできるようにしてみます。

## 実装ステップ
1. ユーザーが生年月日を入力できるようにする
1. 入力された生年月日から星座を判定する関数を作る
1. 今日の日付でAPIへリクエストして、12星座分の結果一覧を取得する
1. その中からユーザーの星座に一致する占い結果を画面に表示する

### 生年月日の入力エリアを作る
スマホで操作しやすいUI、IEやmacのSafariにも対応した設計など考えればキリがなくてどんどん時間が過ぎてしまう。（面白いけど）

この辺りはサイトのターゲティングなどにも関わりそうなので今度じっくり考えてみたいです。


今回、ベースはシンプルに下記のようにします。IEでは対応しておらず、テキストボックスになってしまうらしいです。

``` vue
    <label for="birthday">生年月日</label>
    <input type="date" name="birthday" />
```


参考）

https://ics.media/entry/11221/

https://www.gaji.jp/blog/2020/06/25/4257/

https://www.gaji.jp/blog/2021/05/25/7399/

GPTにアドバイスを求めたらアドバイスをくれたので修正。

- v-model="birthday"
    - Vueの 双方向バインディング。入力された値が birthday に入る
- :max="maxDate"
    - 今日より未来の日付を選べないようにするための設定。Vueの v-bind で max 属性に値をバインドするとよい。

```vue
<div class="p-birthday">
  <label for="birthday">生年月日</label>
  <input
    type="date"
    id="birthday"
    name="birthday"
    v-model="birthday"
    :max="today"
  />
</div>

```

入力された `birthday`を使って星座を判別したいので、これも値を保持できるようにする。

## 入力された生年月日から星座を判定する関数を作る

星座（12星座）は「太陽がどの位置にあるか（黄道十二宮）」に基づいて、
1年をだいたい約30日ずつ12個に分けて決められている。

https://www.japandix.jp/topics/zodiac/001/

https://honkaku-uranai.jp/article/8589/

本来は生まれた年によって星座の境目は変わるが、今回は考慮しないものとする。

| No. |  星座 |     目安の誕生日    |
| :-: | :-: | :-----------: |
|  1  | 牡羊座 |  3月21日〜4月19日  |
|  2  | 牡牛座 |  4月20日〜5月20日  |
|  3  | 双子座 |  5月21日〜6月21日  |
|  4  |  蟹座 |  6月22日〜7月22日  |
|  5  | 獅子座 |  7月23日〜8月22日  |
|  6  | 乙女座 |  8月23日〜9月22日  |
|  7  | 天秤座 |  9月23日〜10月23日 |
|  8  |  蠍座 | 10月24日〜11月22日 |
|  9  | 射手座 | 11月23日〜12月21日 |
|  10 | 山羊座 |  12月22日〜1月20日 |
|  11 | 水瓶座 |  1月21日〜2月18日  |
|  12 |  魚座 |  2月19日〜3月20日  |

たとえば、入力された`birthday` の月が3で日が21〜31の時、または、月が4で日が1〜19の時は牡羊座とする。

```typescript
// 誕生日のデータを文字列としてリアクティブに保持する
const birthday = ref('')
// 今日の日付 'YYYY-MM-DD' 形式 にする
const today = new Date().toISOString().split('T')[0]

// リアクティブなbirthdayに基づく Date オブジェクトを作る（＝computedを使う）
const birthDate = computed(() => new Date(birthday.value))

// 月・日を取得（リアクティブに）
const month = computed(() => birthDate.value.getMonth() + 1)
const day = computed(() => birthDate.value.getDate())
```
※Computedについて

https://ja.vuejs.org/guide/essentials/computed

| やりたいこと       | 使うAPI      | 使い方                     |
| ------------------ | ---------- | ------------------------ |
| 入力された値（文字列など）を保持 | `ref`      | 入力フォームの状態管理に使う           |
| 他の値から計算された結果を保持 | `computed` | 依存してる値が変わったら再計算してくれる |

星座判定のロジックに戻ると、

> 入力された`birthday` の月が3で日が21〜31の時、または、月が4で日が1〜19の時は牡羊座とする。

だから、

``` typescript 
  if ((month === 3 && day >= 21) || (month === 4 && day <= 19)) {
    return '牡羊座'
  }
```

GPTに、同じ容量で他の星座についてのロジックを書いてもらったら`if 文の省略記法`を教えてくれた
💡

:::note info
- {} を省略した if 文
中に「1文だけ」あるときは、実はこんなふうに書いてもOK
` if ((month === 3 && day >= 21) || (month === 4 && day <= 19)) return '牡羊座' `
:::


ただ、このままだとエラーが出る。

:::note alert
Must use `.value` to read or write the value wrapped by `computed()`.

'ComputedRef<number>' 型と 'number' 型が重複していないため、この比較は意図したとおりに表示されない可能性があります。
:::

これは`month`っていうリアクティブな箱と3という数字を比較してしまっているため。
monthの中の値を取り出さないと比較できない。

```typescript
if ((month.value === 3 && day.value >= 21) || (month.value === 4 && day.value <= 19)) return '牡羊座'
```

冗長なので変数を使う！

```typescript
const zodiacSign = computed(() => {
  const m = month.value
  const d = day.value

  if ((m === 3 && d >= 21) || (m === 4 && d <= 19)) return '牡羊座'
  if ((m === 4 && d >= 20) || (m === 5 && d <= 20)) return '牡牛座'
  if ((m === 5 && d >= 21) || (m === 6 && d <= 21)) return '双子座'
  if ((m === 6 && d >= 22) || (m === 7 && d <= 22)) return '蟹座'
  if ((m === 7 && d >= 23) || (m === 8 && d <= 22)) return '獅子座'
  if ((m === 8 && d >= 23) || (m === 9 && d <= 22)) return '乙女座'
  if ((m === 9 && d >= 23) || (m === 10 && d <= 23)) return '天秤座'
  if ((m === 10 && d >= 24) || (m === 11 && d <= 22)) return '蠍座'
  if ((m === 11 && d >= 23) || (m === 12 && d <= 21)) return '射手座'
  if ((m === 12 && d >= 22) || (m === 1 && d <= 20)) return '山羊座'
  if ((m === 1 && d >= 21) || (m === 2 && d <= 18)) return '水瓶座'
  if ((m === 2 && d >= 19) || (m === 3 && d <= 20)) return '魚座'
  return ''
})
```

長くなったので記事を分けます！