vueのライフサイクルでcreated時にapiから取得したデータを画面に表示したい
ある時、「画面にapiから取得したデータを描画したい!」と思いました。ちなみにapiから取得が失敗することもあるのでその出しわけもしたい。。。
変更前
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との通信中はメッセージが空白の状態で表示されてしまう
[画面ロード時]
[1秒後]
問題点
- データがnullの状態で画面に描画されてしまっている
- errorの条件分岐が見づらい
- 一つのファイルが煩雑
解決方法
- api通信中は[loading...]と表示させる
- refを使い、共通コンポーネントからメソッドを呼び出す
- slotを使いapiのステータスに応じて表示を使い出し分ける
変更後
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>
./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>
こうすることでローディング状態、api通信成功、api通信失敗に応じて画面をだしけることができ、他のコンポーネントからも使うことが可能になります。