Vue.jsでBMIを計算するアプリを作ってみた

#はじめに
文系出身エンジニアです。1年目です。

とある経緯でVue.jsを勉強する機会があったので、その際に作った、「身長と体重を入力すると、BMIを計算してくれる」というシンプルなアプリを作ったので、その記録を残します。

Vue.jsはおろか、Javascriptすらあやうい新人エンジニアなので、間違っているところ、改良すべきところがあったら、~~優しめに~~遠慮なくご指摘いただきたく存じます。

#環境
- MacOS v
- Node.js v
- npm V

#基本のプロジェクトをインストール

基本となるプロジェクトは以下のチュートリアルを実行してゲットしました。

[**vue-cliで始めるVue.jsチュートリアル (1)**](https://qiita.com/sin_tanaka/items/29769266b3b078ea0f7c)

バージョンの関係か、この記事で`src/components/Hello.vue`と記載されているものは、実際は`HelloWorld.vue`となりますが、使わないので気にしないでください。

#完成イメージ

![ezgif-2-8ce12c2cf323.gif](https://qiita-image-store.s3.amazonaws.com/0/308235/16e4944d-eb24-2a3f-6d9a-7241a9d12c8f.gif)

ポイントは、以下の3つです。
- 身長と体重のinputが、リアルタイムで上に反映されていること
- `Figure out`ボタンを押すと、メッセージと共にBMIが表示されること
- **デモで入力されている身長と体重は私のものではないこと**


#コンポーネント作成
##コンポーネントファイルの作成
コンポーネントファイルは、`src/components`内に、`.vue`という拡張子を付けて作成します。
以下は一例です。

```
$ touch {作成したプロジェクトのディレクトリ}/src/components/BmiFigureout.vue
```
そもそもコンポーネントとはなんぞやという方には、以下の記事がおすすめです。
[**Vue.jsコンポーネント入門**](https://www.hypertextcandy.com/vuejs-components-introduction-environment-setting)

このファイルは長いので、解説は以下の3つに分けます。
- 見た目(html)
- 処理(javascript)
- 装飾(css)

##見た目を書く
まずは、`<template>`タグ内に、htmlで見た目の部分を書いていきます。

```html:src/components/BmiFigureout.vue

<template>
    <div>
        <form id="input">
            <p>Your hight : {{ hight }} cm</p>
            <p>Your weihgt : {{ weight }} kg</p>
            <p>Input your hight here! -> <input type="text" v-model="hight"></p>
            <p>Input your weight here! -> <input type="text" v-model="weight"></p>
            <p><button class="figureout_btn" @click="emitFigureOut">Figure Out</button></p>
        </form>
    </div>
</template>
```
ここでは、身長と体重のinputと、入力した値をリアルタイムで表示する部分、BMIを算出するためのボタンを表しています。

#####入力した値をリアルタイムで反映
`{{ hight }}`と`{{ weight }}`にそれぞれ入力した値が反映されます。

#####身長と体重のinput
反映する動きは、`v-on:input="updateWeight($event.target.value)`に書かれていて、具体的な処理は`<script>`タグ内の`updateWeight`というメソッドに書かれています。

#####BMIを算出するためのボタン
`<button>`タグ内に書かれています。
ここで注目する箇所は、`@click="emitFigureOut"`です。
このボタンをクリックすると、`emitFigureOut`というメソッドが動きます。処理内容については次の項目で説明します。

#####追記
`v-model="height"`は`:value="height" v-on:input="height =($event.target.value")`の省略形だそうです。
教えてくださった[@akicho8](https://qiita.com/akicho8)さん、ありがとうございます！


##処理を書く
`<script>`タグ内に、Javascriptで処理の部分を書いていきます。

```javascript:src/components/BmiFigureout.vue
<script>
export default {
    name: 'bmi',
    data () {
        return {
            hight : "",
            weight : ""
        }
    },
    methods: {
        updateHight: function(value){
            this.hight = value
        },
        updateWeight: function(value){
            this.weight = value
        },
        emitFigureOut () {
            var weightKg = this.weight
            var hightM = this.hight/100
            var bmiValue = weightKg / (hightM * hightM)
            this.$emit('figure-out', bmiValue)
        }
    }
}
</script>
```
#####dataについて
ここが無いとだめってことはわかりました。
どこかに値を返してそうなんですが、どこに返っているのかわかりません（小声）

#####`updateWeight`について
入力された値をリアルタイムで表示するメソッドです。
詳しいことはわかりません。調査中です。
`updateHight`も同様です。

#####`emitFigureOut`について
emitやイベント発行についてはこちらの記事で勉強しました。
[Vue.jsコンポーネント入門 (4) $emitによるイベントの発行](https://www.hypertextcandy.com/vuejs-components-introduction-emit-events)

身長と体重をそれぞれ変数に入れて、その変数を用いて計算したBMIの値を、`bmiValue`という変数に入れます。

#####イベントの発行

```javascript:src/components/BmiFigureout.vueの下の方
this.$emit('figure-out', bmiValue)
```
`this.$emit()`でイベントの発行を表します。
第一引数には「イベント名」、第二引数にはイベントハンドラー（`main.js`）に渡したい値を入れます。


##飾りを書く
`<style>`タグ内に、cssで飾り付けしていきます。
なくてもいいです。

```css:src/components/BmiFigureout.vue

<style>
#input,#bmi {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 30px;
}
h1,h2 {
  font-weight: normal;
}
.figureout_btn{
    display: inline-block;
    padding: 0.5em 1em;
    text-decoration: none;
    font-weight: bold;
    font-size: 80%;
    background: #668ad8;
    color: #FFF;
    border-bottom: solid 4px #627295;
    border-radius: 3px;
}
.figureout_btn {
    -ms-transform: translateY(4px);
    -webkit-transform: translateY(4px);
    transform: translateY(4px);/*下に動く*/
    border-bottom: none;/*線を消す*/
}
</style>
```


#イベントの処理を記述
`main.js`に、イベントの処理を記述します。

```javascript:src/main.js

import Vue from 'vue'
import Bmi from './components/BmiFigureOut.vue'

Vue.config.productionTip = false

const bmi = new Vue ({
  el: '#bmi',
  data : {
    msg: '🐽 Figure out your BMI 🐽',
    msgBmi: '',
    bmiValue: ''
  },
  components: { 
    Bmi
  },
  methods: {
    onFigureOut (bmi) {
      this.bmiValue = bmi
      this.msgBmi = 'Your BMI is....'
    }
  }
})
```
#####作成したコンポーネントを読み込む
二行目の`import`文で、作成したコンポーネントを読み込みます。
読み込んだコンポーネントは`Bmi`として認識されます

#####返り値（？）
返す値を`data`に記述します。
`msgBmi`と`bmiValue`は空になってるように見えますが、下の`onFigureOut`メソッドで代入されます。

#####コンポーネントの使用
読み込んだコンポーネントを以下のように記述すると、使用できるようになります。


```javascript:src/main.jsの中盤
components: { 
    Bmi
  },
```

#####`onFigureOut`メソッド
[**イベントの発行**](https://qiita.com/wakiwaki/items/72b03042f26b20482cdc#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E7%99%BA%E8%A1%8C)で発行したイベントを処理する部分です。
`onFigureOut`は、ボタンをクリックすると起こるイベントで、このイベントには、引数（計算されたBMIの値）がありました。
その引数を、`bmiValue`に代入します。
また、`msgBmi`にメッセージも代入します。
このように記述することによって、ボタンが押下されると、返り値（`data`内）に値が代入されます。




#ページにコンポーネントを表示させる
最後に、`index.html`に、コンポーネントを表示していきます。

```html:index.html

<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>BMI</title>
  </head>
  <body>
    <div id='bmi'>
      <h1>{{ msg }}</h1>
      <bmi @figure-out='onFigureOut'></bmi>
      <h2>{{ msgBmi }} {{ bmiValue }}</h2>
    </div>
  </body>
</html>
```
#####コンポーネントの表示方法
以下のコードに注目してください。

```html:index.htmlの<body>タグ内
<bmi @figure-out='onFigureOut'></bmi>
```
`main.js`で読み込んだコンポーネント`Bmi`を`<bmi></bmi>`とタグのように記述することによって、コンポーネントが表示されます。
`@figure-out='onFigureOut'`では、`BmiFigureout.vue`で発行されたイベントを、`main.js`が読み込めるように形を変える役割をしています。

#####`{{ msg }}` `{{ msgBmi }}` `{{ bmiValue }}`
`main.js`から返ってきた`data`の中身がカッコ内`{{ }}`に入ります。


#最後に
勉強しながら分かる範囲で書いてみました。

イメージとしては、
`.vue`ファイル（コンポーネント）を作成
　　　↓
`emit`でイベント発行（今回はボタン押下）
　　　↓
`main.js`でイベントの処理
　　　↓
`index.html`でコンポーネント表示（＋`main.js`がイベントを処理できるように書き換え）

といった具合です。

間違っていることがあれば教えてください。(_ _)