質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

Nuxt.js

Nuxt.jsは、ユニバーサルなSPAが開発可能なVue.jsベースのフレームワーク。UIの描画サポートに特化しており、SSRにおけるサーバーサイドとクライアントサイドのUIレンダリングなどさまざまな機能を持ちます。

Q&A

解決済

1回答

4513閲覧

Nuxt.js: Vuexにstoreされたデータを読み出す前に描画が始まり、正しく描画できないケースがある

Eskee

総合スコア268

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

Nuxt.js

Nuxt.jsは、ユニバーサルなSPAが開発可能なVue.jsベースのフレームワーク。UIの描画サポートに特化しており、SSRにおけるサーバーサイドとクライアントサイドのUIレンダリングなどさまざまな機能を持ちます。

0グッド

1クリップ

投稿2018/08/14 01:53

編集2018/08/14 02:22

Nuxt.jsを使用し、ポートフォリオサイトを作成中です。

バックエンドは、Airtableを利用し、APIをGETのみで利用しています。

Airtableはリレーショナル・データベースをGoogle SpreadSheet風に操作するSaaSですが、他テーブルとリレーションを張る時に、他テーブルのレコードIDを記録しています。

現在テーブルは

  • 作品マスタ(works)
  • クライアントマスタ(clients)
  • 代理店マスタ(agencies)

の3つを作成しています。

routingを使って作品一覧ページ(/works/)と、作品詳細ページ(/works/:id)、クライアント一覧ページ(/clients)を作成しています。

掲題の通り、作品一覧ページとクライアント一覧ページで、おそらくですが、3つのテーブルのデータを読み込む前に描画が始まり、時折エラーがでます。ほんの0.数秒の差で先に3つのテーブルのデータが読み込めていれば、問題なく描画されます。

この読み込みのタイミングを上手くコントロールする方法がわからず、質問させていただきました。

各ファイルの構成

pages/store/index.js

JavaScript

1import axios from 'axios' 2 3export const state = () => ({ 4 works: [], 5 clients: [], 6 agencies: [] 7}) 8 9export const mutations = { 10 setWorks (state, works) { 11 state.works = works 12 }, 13 setClients (state, clients) { 14 state.clients = clients 15 }, 16 setAgencies (state, agencies) { 17 state.agencies = agencies 18 }, 19} 20 21export const actions = { 22 load(ctx) { 23 axios.get("https://api.airtable.com/v0/appxxxxxxxxx/works?api_key=keyxxxxxxxxxxxxxxxx") 24 .then( (res) => { 25 ctx.commit('setWorks', res.data.records ) 26 console.log(res) 27 }) 28 .catch(function (error) { 29 // handle error 30 console.log(error); 31 }) 32 33 axios.get("https://api.airtable.com/v0/appxxxxxxxxx/clients?api_key=keyxxxxxxxxxxxxxxxx") 34 .then( (res) => { 35 ctx.commit('setClients', res.data.records ) 36 console.log(res) 37 }) 38 .catch(function (error) { 39 // handle error 40 console.log(error); 41 }) 42 43 axios.get("https://api.airtable.com/v0/appxxxxxxxxx/agency?api_key=keykeyxxxxxxxxxxxxxxxx") 44 .then( (res) => { 45 ctx.commit('setAgencies', res.data.records ) 46 console.log(res) 47 }) 48 .catch(function (error) { 49 // handle error 50 console.log(error); 51 }) 52 53 } 54}

pages/works/index.vue

JavaScript

1<template lang="pug"> 2// 省略 3</template> 4 5<script> 6import moment from 'moment' 7import _ from 'lodash' 8import fz from 'fuzzaldrin-plus' 9 10export default { 11 data () { 12 return { 13 query: '', 14 mediaTypeQuery: '' 15 } 16 }, 17 computed: { 18 works () { 19 return this.$store.state.works 20 }, 21 clients () { 22 return this.$store.state.clients 23 }, 24 queryResults() { 25 if(!this.query) { 26 return this.works; 27 } 28 const preparedQuery = fz.prepareQuery(this.query); 29 const scores = {}; 30 31 return this.works 32 .map((option, index) => { 33 const scorableFields = [ 34 option.fields.Name, 35 option.fields.mediaType, 36 option.fields.purpose, 37 option.fields.pubDate, 38 ].map(toScore => fz.score(toScore, this.query, { preparedQuery })); 39 40 scores[option.fields.Name] = Math.max(...scorableFields); 41 42 return option; 43 }) 44 .filter(option => scores[option.fields.Name] > 1) 45 .sort((a, b) => scores[b.fields.Name] - scores[a.fields.Name]) 46 ; 47 }, 48 }, 49 methods: { 50 outputPubDate (date) { 51 let newStyle = moment(date, 'YYYY-MM-DD') 52 let output = newStyle.format('YYYY') 53 return output 54 }, 55 getClientName (cid) { 56 let name = _.find(this.clients, (item) => { 57 return _.includes(item.id, cid) 58 }) 59 return name.fields.Name 60 console.log(name) 61 }, 62 clear () { 63 this.query = '' 64 }, 65 setWeb () { 66 this.query = '' 67 this.query = 'Web' 68 }, 69 setMovie () { 70 this.query = '' 71 this.query = 'Movie' 72 }, 73 setPrint () { 74 this.query = '' 75 this.query = 'Print' 76 }, 77 setPhoto () { 78 this.query = '' 79 this.query = 'Photo' 80 }, 81 }, 82 mounted () { 83 this.$store.dispatch('load').then(() => {console.log('dispatched')}) 84 } 85} 86</script>

pages/clients/_id.vue(参考)

JavaScript

1<template lang="pug"> 2// 省略 3</template> 4 5<script> 6import _ from 'lodash' 7import moment from 'moment' 8 9export default { 10 data () { 11 return { 12 work: null 13 } 14 }, 15 computed: { 16 works () { 17 return this.$store.state.works 18 }, 19 clients () { 20 return this.$store.state.clients 21 }, 22 agencies () { 23 return this.$store.state.agencies 24 } 25 }, 26 methods: { 27 outputPubDate (date) { 28 let newStyle = moment(date, 'YYYY-MM-DD') 29 let output = null 30 if(this.work.fields.mediaType === 'Web') { 31 output = newStyle.format('YYYY年M月公開') 32 } else { 33 output = newStyle.format('YYYY年M月') 34 } 35 return output 36 }, 37 getClientName (cid) { 38 let name = _.find(this.clients, (item) => { 39 return _.includes(item.id, cid) 40 }) 41 return name.fields.Name 42 }, 43 getAgencyName (aid) { 44 let name = _.find(this.agencies, (item) => { 45 return _.includes(item.id, aid) 46 }) 47 return name.fields.Name 48 }, 49 getClientOtherWorks (wid) { 50 const name = _.filter(this.clients, (item) => { 51 return _.includes(item.id, wid) 52 }) 53 console.log(name) 54 const relWorks = _.without(name[0].fields.works,this.work.id) // strip this work itself 55 console.log('relWorks: ' + relWorks) 56 57 let works = [] 58 for (let i = 0; i < relWorks.length; i++){ 59 let relWorksName = _.find(this.works, (item) => { 60 return _.includes(item.id, relWorks[i]) 61 }) 62 works.push({ 63 name: relWorksName.fields.Name, 64 url: relWorks[i], 65 image_url: relWorksName.fields.square_image 66 }) 67 } 68 console.log('works: ' + JSON.stringify(works)) 69 return works 70 }, 71 }, 72 mounted () { 73 for (let work of this.$store.state.works) { 74 if (work.id == this.$route.params.id) { 75 this.work = work 76 } 77 } 78 if (!this.work) { // 記事が存在しない場合トップへ 79 this.$router.push("/") 80 } 81 this.$store.dispatch('load') 82 } 83} 84</script> 85 86<style lang="stylus"> 87 88</style>

pages/clients/index.vue

JavaScript

1<template lang="pug"> 2// 省略 3</template> 4 5<script> 6import _ from 'lodash' 7import fz from 'fuzzaldrin-plus' 8 9export default { 10 data () { 11 return { 12 query: '' 13 } 14 }, 15 computed: { 16 works () { 17 return this.$store.state.works 18 }, 19 clients () { 20 return this.$store.state.clients 21 }, 22 queryResults() { 23 if(!this.query) { 24 return this.clients; 25 } 26 const preparedQuery = fz.prepareQuery(this.query); 27 const scores = {}; 28 29 return this.clients 30 .map((option, index) => { 31 const scorableFields = [ 32 option.fields.Name, 33 option.fields.industry, 34 option.fields.businessType 35 ].map(toScore => fz.score(toScore, this.query, { preparedQuery })); 36 37 scores[option.fields.Name] = Math.max(...scorableFields); 38 39 return option; 40 }) 41 .filter(option => scores[option.fields.Name] > 1) 42 .sort((a, b) => scores[b.fields.Name] - scores[a.fields.Name]) 43 ; 44 } 45 }, 46 methods: { 47 getWorkName: function(wid) { 48 const name = _.find(this.works, (item) => { 49 return _.includes(item.id, wid) 50 }) 51 return `${name.fields.Name}` 52 }, 53 getWorkThumb: function(wid) { 54 const url = _.find(this.works, (item) => { 55 return _.includes(item.id, wid) 56 }) 57 if(url.fields.imagePC) { 58 return url.fields.square_image 59 } 60 } 61 }, 62 mounted () { 63 this.$store.dispatch('load').then(() => {console.log('dispatched')}) 64 } 65} 66</script> 67 68<style lang="stylus"> 69 70</style> 71

スクリーンショット

pages/works/index.vueの成功時

pages/works/index.vueの成功時

pages/works/index.vueの失敗時

pages/works/index.vueの失敗時
4~5回に1回は、このようになります。再現性は一定しません。

pages/works/index.vue成功時のConsole

イメージ説明

pages/works/index.vue失敗時のConsole

イメージ説明

このソースを書くにあたり参考にしたページ

Nuxt.js 入門 #1 – Nuxt.js によるブログアプリ制作 – chatbox blog

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

getClientName() 内の _.find()undefined を返しているようです。
getClientName() に渡される cid の値は this.clients のどれかに必ず一致しますか?

追記

axiosPromise ベースの API です。
Promise.all() を使用すれば、すべてのレスポンスを一度に受け取ることができます。
以下のコードを試してみてください。

js

1export const actions = { 2 load(ctx) { 3 Promise.all([ 4 axios.get("https://api.airtable.com/v0/appxxxxxxxxx/works?api_key=keyxxxxxxxxxxxxxxxx"), 5 axios.get("https://api.airtable.com/v0/appxxxxxxxxx/clients?api_key=keyxxxxxxxxxxxxxxxx"), 6 axios.get("https://api.airtable.com/v0/appxxxxxxxxx/agency?api_key=keykeyxxxxxxxxxxxxxxxx"), 7 ]) 8 .then(responses => { 9 console.log(responses) 10 ctx.commit('setWorks', responses[0].data.records) 11 ctx.commit('setClients', responses[1].data.records) 12 ctx.commit('setAgencies', responses[2].data.records) 13 }, reason => { 14 console.error(reason) 15 }) 16 }, 17}

投稿2018/08/14 02:14

編集2018/08/14 02:34
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Eskee

2018/08/14 02:19

はい、必ず一致します。Airtableの仕様で、リレーションで結んでいるため、一致しないレコードというのは存在しません。 そこでコケているのは、worksのGETは成功していて、clientsのGETが未完了だからだと考えています。
Eskee

2018/08/14 02:41

## 追記をうけて ありがとうございます。 そのように記述したところ、何度もリロードしても必ず表示されるようになりました! axiosはそのように書くことができるのですね。Promiseについては勉強が全然足りておらず、お恥ずかしい限りです。これを機会に、初歩から勉強し直したいと思います。 ありがとうございました。
退会済みユーザー

退会済みユーザー

2018/08/14 02:44

お役に立ててよかったです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問