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

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

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

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

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

1377閲覧

v-for中に登場する「子コンポーネント」内に定義されたメソッドを、親から$refsで呼びたい。

bokupiroki

総合スコア54

Vue.js

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

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2022/01/07 14:36

編集2022/01/09 03:42

前提・実現したいこと

  • Vue.jsで、子コンポーネント内に定義されたメソッドを親コンポーネントから呼び出したい
  • 子コンポーネントは親コンポーネントのループ内で呼び出している
  • 呼び出せずにエラーになる

具体的には、記事一覧ページに表示されている個々の記事に編集ボタンを設け、
ダイアログ上で内容を更新できるようにしようとしています。

発生している問題・エラーメッセージ

記事更新しようとすると、ブラウザで見た時にコンソールに次のようなエラーが出ます。

app.js:36742 [Vue warn]: Error in v-on handler: "TypeError: Cannot read properties of undefined (reading 'update')"

該当のソースコード

↓親コンポーネント

vue

1<template> 2<v-card v-for="(inquiry,index) in inquiries" :key="inquiry.id" class="inquiry" flat :id="inquiry.id"> 3 <v-toolbar color="primary" dark dense> 4 <v-dialog max-width="90%" scrollable> 5 <template v-slot:activator="{ on, attrs }"> 6 <v-btn icon v-bind="attrs" v-on="on"> 7 <v-icon>mdi-square-edit-outline</v-icon> 8 </v-btn> 9 </template> 10 <template v-slot:default="dialog"> 11 <v-card> 12 <v-toolbar color="primary" dark>記録編集</v-toolbar> 13 <v-card-text> 14 <RecordForm :inquiry="inquiry" ref="RecordForm"></RecordForm> 15 </v-card-text> 16 <v-card-actions class="end"> 17 <v-btn text @click="edit(index,inquiry.id)">更新</v-btn> 18 <v-btn text @click="dialog.value = false">閉じる</v-btn> 19 </v-card-actions> 20 </v-card> 21 </template> 22 </v-dialog> 23 </v-toolbar> 24</v-card> 25</template> 26 27<script> 28 import RecordForm from '../layout/RecordForm' 29 30 export default { 31 components: { 32 RecordForm, 33 }, 34 mounted() { 35 axios.get('/api/inquiries/archive') 36 .then(response => { 37 this.inquiries = response.data.data.reverse() 38 }) 39 }, 40 methods: { 41 edit(index, id) { 42 console.log(index) 43 this.$refs.RecordForm[index].update(id) 44 }, 45 }, 46 } 47</script> 48

↓子コンポーネント(RecordForm.vue)

vue

1//js部分 2<script> 3 export default { 4 name: 'RecordForm', 5 props: ["inquiry"], 6 7 //中略 8 9 methods: { 10 postData(id) { 11 let postData = { 12 //(中略) 13 } 14 return postData 15 }, 16 update(id) { 17 let postData = this.postData(id) 18 axios.post('/api/inquiries/edit', postData) 19 .then(response => { 20 alert('更新しました。'); 21 console.log(response); 22 }) 23 }, 24 } 25 } 26</script>

試したこと

ここでは省略していますが、
親コンポーネントにおけるループ外の新規投稿ボタンから「子コンポーネント内に定義された新規投稿メソッド」を呼び出すことはできました。

なのでループの場合のみ起こる現象だと考えています。

下記の記事
https://qiita.com/gigazombie/items/6f53a1de17c370787eac
を見て、

  • 親コンポーネントから子コンポーネントのメソッドを呼びたい時は$refsを使えば可能
  • v-forの引数として提供されているindexを渡す必要がある

というところまでわかったのですが、その先に進めない状態です。

どなたか知恵を貸していただきたいです。

補足情報(FW/ツールのバージョンなど)

Vue2
Laravel8

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

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

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

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

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

naomi3

2022/01/09 06:28

正解ではないのでここに書かせていただきますが、気になったのは、:id="inquiry.id"です。 inquiry.idの値がそのままコンポーネントのHTMLのid属性値となり、1つのHTMLで「全ての要素のid属性値」はユニークでなければなりませんが、そこは大丈夫でしょうか?
naomi3

2022/01/09 07:25

v-card要素が入れ子になっていますが、それは大丈夫ですか? 子コンポーネントの削除や入れ替えはないですか?それによってindexがずれる可能性があります。
naomi3

2022/01/09 07:33

「@click="dialog.value = false"」ダイアログのインスタンス管理は、1つだけで行っておられるかのようですが、ダイアログもv-forの中にあるので、「回数分=子コンポーネントの数」だけのインスタンスがあります。それも大丈夫ですか?
bokupiroki

2022/01/10 07:18

ありがとうございます。 :id="inquiry.id"の部分は各記事のidがそれぞれ出力されるだけなので、重複せずユニークになっています。 v-card要素が入れ子になっていますが、それは大丈夫ですか? →おそらく大丈夫です。入れ子になっていますが、子要素はボタンをクリックしたら表示されるダイアログなので。 「「@click="dialog.value = false"」ダイアログのインスタンス管理は、1つだけで行っておられるかのようですが、ダイアログもv-forの中にあるので~」 →単純にその時「true」になっているダイアログがあればとにかくfalseにして非表示にするイメージなので問題がない認識です。  実際に該当部分を削除してみても、今回の事象は解決しませんでした。
naomi3

2022/01/10 15:51

ダイアログは表示されていないときはHTML要素として存在していず、表示されるときのみ生成されるかと思います。(高々1つのみ存在) これはダイアログが含む子コンポーネントRecordFormも同様です。 ところが、子コンポーネントRecordFormがv-forの回数分だけ複数個存在しているかのようにコーディングされています。this.$refs.RecordForm[index] Developer Toolを使って、ダイアログを開くときに実際に生成されているソース中のindex,inquiry.idを確認することをお勧めします。
bokupiroki

2022/01/11 06:50

解決しました。 「子コンポーネントRecordFormがv-forの回数分だけ複数個存在しているかのようにコーディングされています。」 →これ自体は問題なかったようです。 <RecordForm :inquiry="inquiry" ref="RecordForm"></RecordForm> ここの「ref="RecordForm"」が問題だったようです。 ループ中にいくつもこのコンポーネントが登場するのに、 全部ref="RecordForm"で名前が同じなので、どれを参照すればいいかが分からず エラーが出ていたものかと思われます。 ありがとうございました。
guest

回答1

0

自己解決

解決しました。
原因は、ループ中に登場するすべてのコンポーネントに、同じ名前のrefが割り当てられていることでした。
ref自体にindex番号を付与してユニークにしました。
それぞれのダイアログ内のボタンを押したとき、どのrefを参照すればいいかが明確になったことにより動作しました。

<RecordForm :inquiry="inquiry" ref="RecordForm"></RecordForm>

<RecordForm :inquiry="inquiry" :ref="RecordForm + index"></RecordForm>

親コンポーネント

vue

1<template> 2<v-card v-for="(inquiry,index) in inquiries" :key="inquiry.id" class="inquiry" flat :id="inquiry.id"> 3 <v-toolbar color="primary" dark dense> 4 <v-dialog max-width="90%" scrollable> 5 <template v-slot:activator="{ on, attrs }"> 6 <v-btn icon v-bind="attrs" v-on="on"> 7 <v-icon>mdi-square-edit-outline</v-icon> 8 </v-btn> 9 </template> 10 <template v-slot:default="dialog"> 11 <v-card> 12 <v-toolbar color="primary" dark>記録編集</v-toolbar> 13 <v-card-text> 14 <RecordForm :inquiry="inquiry" :ref="RecordForm + index"></RecordForm><!--←ループ中のref自体がユニークになるようにする--> 15 </v-card-text> 16 <v-card-actions class="end"> 17 <v-btn text @click="edit(index,inquiry.id)">更新</v-btn> 18 <v-btn text @click="dialog.value = false">閉じる</v-btn> 19 </v-card-actions> 20 </v-card> 21 </template> 22 </v-dialog> 23 </v-toolbar> 24</v-card> 25</template> 26 27<script> 28 import RecordForm from '../layout/RecordForm' 29 30 export default { 31 components: { 32 RecordForm, 33 }, 34 mounted() { 35 axios.get('/api/inquiries/archive') 36 .then(response => { 37 this.inquiries = response.data.data.reverse() 38 }) 39 }, 40 methods: { 41 edit(index, id) { 42 const RecordFormStr = 'RecordForm'+index 43 this.$refs[RecordFormStr][0].update(id)//なぜかオブジェクト形式で渡されるので0番を指定 44 }, 45 }, 46 } 47</script>

投稿2022/01/11 06:47

bokupiroki

総合スコア54

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問