vue.jsにてライフサイクルのcreatedで取得したデータを画面に表示する方法

# vueのライフサイクルでcreated時にapiから取得したデータを画面に表示したい

ある時、「画面にapiから取得したデータを描画したい！」と思いました。ちなみにapiから取得が失敗することもあるのでその出しわけもしたい。。。
## 変更前

```vuejs:App.vue
<template>
  <div>
    <template v-if="!errorMessage">
      <p>
        メッセージ: {{msg}}
      </p>
    </template>
    <template v-else>
      <p>
        {{errorMessage}}
      </p>
    </template>
  </div>
</template>
<script>
function apiTestFunc(value) {
  return new Promise((resolve, reject) => {
      setTimeout(() => {
        const n = value;
        if(n < 100) {
          resolve('100より小さい')
        } else {
          reject('100より大きい')
        }
      }, 1000)
    })
}
export default {
  data() {
    return {
      msg: null,
      errorMessage: ''
    }
  },
  async created() {
    await apiTestFunc(99) // 101を入れると.catchの方に入る
      .then(value => {
        this.msg = value;
      })
      .catch(error => {
        this.errorMessage = error;
      })
  }
}
</script>
```
そうするとapiとの通信中はメッセージが空白の状態で表示されてしまう
[画面ロード時]
![スクリーンショット 2021-06-01 12.35.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528444/b751901d-90e9-c74a-3fc5-8d42cc329614.png)
[１秒後]
![スクリーンショット 2021-06-01 12.37.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528444/8209e988-fc1c-5c35-fded-578bbd8f4991.png)

### 問題点

- データがnullの状態で画面に描画されてしまっている
- errorの条件分岐が見づらい
- 一つのファイルが煩雑


### 解決方法
- api通信中は[loading...]と表示させる
- refを使い、共通コンポーネントからメソッドを呼び出す
- slotを使いapiのステータスに応じて表示を使い出し分ける

## 変更後
```vuejs:App.vue
<template>
  <api-status 
    ref="apiStatus"
    :error-message="errorMessage"
  >
    <template #data>
      <p>
        メッセージ: {{msg}}
      </p>
    </template>
  </api-status>
</template>
<script>
import ApiStatus from './common/ApiStatus'
function apiTestFunc(value) {
  return new Promise((resolve, reject) => {
      setTimeout(() => {
        const n = value;
        if(n < 100) {
          resolve('100より小さい')
        } else {
          reject('100より大きい')
        }
      }, 1000)
    })
}
export default {
  components: {
    ApiStatus
  },
  data() {
    return {
      msg: null,
      errorMessage: ''
    }
  },
  async created() {
    await apiTestFunc(99) // 101を入れると.catchの方に入る
      .then(value => {
        this.msg = value;
        this.$refs.apiStatus.changeSuccess();
      })
      .catch(error => {
        this.errorMessage = error;
        this.$refs.apiStatus.changeError();
      })
  }
}
</script>

```

```vuejs:./common/ApiStatus.vue
<template>
  <div>
    <div v-if="apiStatus.isSuccess">
      <slot name="data"></slot>
    </div>
    <div v-if="apiStatus.isLoading">
      Loading...
    </div>
    <div v-if="apiStatus.isError">
      {{errorMessage}}
    </div>
  </div>
</template>
<script>
export default {
  props: {
    errorMessage: {
      required: false,
      type: String,
      default: ''
    }
  },
  data: () => ({
    /**
     * ローディング状態
     */
    apiStatus: {
      isLoading: true,
      isSuccess: false,
      isError: false,
    }
  }),
  methods: {
    /**
     * apiが成功した時
     */
    changeSuccess() {
      this.apiStatus = {
        isLoading: false,
        isSuccess: true,
        isError: false,
      }
    },
    /**
     * apiが失敗した時
     */
    changeError() {
      this.apiStatus = {
        isLoading: false,
        isSuccess: false,
        isError: true,
      }
    },
  }
}
</script>

```
[画面ロード時]
![スクリーンショット 2021-06-01 12.47.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528444/7f74c255-ad30-6029-aa31-4d020aad5627.png)

[１秒後]
![スクリーンショット 2021-06-01 12.37.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528444/8209e988-fc1c-5c35-fded-578bbd8f4991.png)

こうすることでローディング状態、api通信成功、api通信失敗に応じて画面をだしけることができ、他のコンポーネントからも使うことが可能になります。