v-forを使わずにarrayを展開する[Vue.js] ガチャガチャ英単語と日本語
はじめに
template内で配列を展開する場合はv-forで展開ができます。 今回やりたかったのはLaravelから取った配列データを保存すること。 Vueにまだ詳しくないからまったくやりかたのイメージがつかなかったので完成するまでに2日かかりました。 色々な書き方を試して、ググって書いてエラーが出てロジックが間違えてるかもと思ってゼロからやり直すことも考えました。
ためしたこと
まずは既存のカラムにtextareaもしくはinputにv-modelでv-forを使ってデータを渡す。展開して保存。inputと併用。 →できない。できるわけない。
新しくpostテーブルにselected_wordsカラムを追加した。配列データを別のテーブルのカラムにstringで登録することにしました。 →たぶん正しい。けど、どうすれば保存できるのかわからず苦戦。
Laravelのコントローラー内でメソッドの戻り値をそのまま保存 →できない。できるのか?
とりあえずいろいろ調べて試してを繰り返した。。。。
2日後。
Vueの基礎から調べなおして、dataに直接書こうとしていたことが間違いだと気づきました。色々間違えてましたがここが大きな間違いです。 dataの値は直接変更はできない。基本ここで何らかの処理を行えない。 もし、dataプロパティに何らかの処理を加えたい場合はcomputedプロパティを使います。
ようやくデータを保存できるようになったのですが、この2つのエラーが出てどうしても解決できませんでした。
[Vue warn]: Error in render: "TypeError: Cannot read property 'word' of undefined"
TypeError: Cannot read property 'word' of undefined
export default new Vuex.Store({ state: { words: [], }, mutations: { loadEnglishWord(state, data ) { state.words = data }, }, actions: { loadEnglishWord({commit}) { axios.get("/en-words") .then((response) => { commit('loadEnglishWord', response.data.data) // console.log(response.data.data); }) .catch(function (error) { console.log(error); }); }, })
↓の書き方でも問題なく動くのですが'word' of undefinedのエラーがでます。 this.$store.state.words[0] の値が undefined になっているときに undefined.word とプロパティを参照することになるため表示される警告です。 実際にはデータを取れてるので何が間違えてるのか、どうすれば解決できるのかが全く分かりませんでした。
EnglishCreateForm.vue
<template> <div > {{ loadSelectedWord }} </div> </template> <script> import {mapState} from 'vuex' export default { data() { return { post:{ selected_words: '' }, } }, computed: { ...mapState(['words']), loadSelectedWord(){ let val = this.$store.state.words[0].word + '/' + this.$store.state.words[1].word + '/'+ this.$store.state.words[2].word return this.post.selected_words = val } }, } </script>
computedのloadSelectedWordを下記のように変更。結果は変わらないのですが、エラーが消えました。 もしwords[0]が存在するならword、ないならブランク。if文ですね。元々確実にデータは取れてたのでブランクになることは無いのですが、エラーが消えてすっきりしました。
EnglishCreateForm.vue
loadSelectedWord(){ let val = (this.$store.state.words[0]?.word || '') + '/' + (this.$store.state.words[1]?.word || '') + '/'+ (this.$store.state.words[2]?.word || '') return this.post.selected_words = val }
↓は別のファイルにTwiiter投稿用に書いていたもの。これもうごきます。動くのですが'word' of undefinedのエラーがでます。
EnglishWord.vue
<a :href="'https://twitter.com/intent/tweet?text=【次の3つで文章を作ってね】%0A'+ words[0].word+ '/' + words[0].word+ '/'+ words[0].word+'%0A%0A&hashtags=ガチャガチャ日本語'" id="tw" onClick="window.open(encodeURI(decodeURI(this.href)), 'tweetwindow', 'width=650, height=470, personalbar=0, toolbar=0, scrollbars=1, sizable=1'); return false;" rel="nofollow" class="twitter-link"> <p class="text-sm font-medium leading-normal text-center py-2"> Tweetする </p> </a>
訂正1。
dataプロパティにword1、word2、word3を作ってcomputedプロパティにTweetword1,2,3を作成。条件分岐させた戻り値を返します。'word' of undefinedのエラーが消えました。すっきりしました。ただ同じような関数を繰り返し書いているのでもう少し修正。
EnglishWord.vue
<a :href="'https://twitter.com/intent/tweet?text=【次の3つで文章を作ってね】%0A'+ Tweetword1 + '/' + Tweetword2 + '/'+ Tweetword3 +'%0A%0A&hashtags=ガチャガチャ日本語'" id="tw" onClick="window.open(encodeURI(decodeURI(this.href)), 'tweetwindow', 'width=650, height=470, personalbar=0, toolbar=0, scrollbars=1, sizable=1'); return false;" rel="nofollow" class="twitter-link"> <p class="text-sm font-medium leading-normal text-center py-2"> Tweetする </p> </a> <script> import {mapState} from 'vuex' import JPCreateForm from '../components/JPCreateForm' export default { data: function () { return { post:{ post:'' , }, word1:'', word2:'', word3:'' }; }, created() { //actionsをdispatch this.$store.dispatch('loadJapaneseWord') this.$store.dispatch('loadJapaneseWordPost') }, computed: { ...mapState(['words','posts']), Tweetword1() { let word = this.$store.state.words[0]?.word || '' return this.word1 = word }, Tweetword2() { let word = this.$store.state.words[1]?.word || '' return this.word2 = word }, Tweetword3() { let word = this.$store.state.words[2]?.word || '' return this.word3 = word }, }, }; </script>
訂正2。
dataプロパティに forTweetWord:'',を作ってcomputedプロパティはTweetWordだけ。word1,2,3をそれぞれ条件分岐させたものをまとめた戻り値を返します。さっきよりもすっきりしました。エラーはありません。HTMLにもTweetWord だけを書けばいいので綺麗になりました。
EnglishWord.vue
<a :href="'https://twitter.com/intent/tweet?text=【次の3つで文章を作ってね】%0A'+ TweetWord + '%0A%0A&hashtags=ガチャガチャ日本語'" id="tw" onClick="window.open(encodeURI(decodeURI(this.href)), 'tweetwindow', 'width=650, height=470, personalbar=0, toolbar=0, scrollbars=1, sizable=1'); return false;" rel="nofollow" class="twitter-link"> <p class="text-sm font-medium leading-normal text-center py-2"> Tweetする </p> </a> <script> import {mapState} from 'vuex' import JPCreateForm from '../components/JPCreateForm' export default { data: function () { return { post:{ post:'' , }, forTweetWord:'', }; }, created() { //actionsをdispatch this.$store.dispatch('loadJapaneseWord') this.$store.dispatch('loadJapaneseWordPost') }, computed: { ...mapState(['words','posts']), TweetWord() { let word = this.$store.state.words[0]?.word || '' let word1 = this.$store.state.words[1]?.word || '' let word2 = this.$store.state.words[2]?.word || '' let total = word +'/'+ word1 +'/'+ word2 return this.forTweetWord = total } }, }; </script>
おわりに
VueはLaravelと違ってどこが具体的に間違えてるかわからず苦労しました。Console以外のデバッグの方法があれば知りたいです。。。