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

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

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

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

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

Q&A

解決済

1回答

907閲覧

【Vue x Firestore】子コンポーネントから親コンポーネントで受けたデータをもとに条件をつけてリストを再表示させたい

TMTN

総合スコア53

Vue.js

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

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

0グッド

0クリップ

投稿2021/06/05 11:28

編集2021/06/07 11:32

#子コンポーネントから親コンポーネントで受けたデータをもとに条件をつけてリストを再表示させたい

現在、post.vue(子コンポーネント)にてinputタグで入力フォームを作成し、
そこで入力された値を$emitでboard.vue(親コンポーネント)に渡しています。(値を取得できていることは確認済)

イメージ説明

通常投稿一覧に投稿されたpostを「時間(降順)」で表示させておりますが、
post.vue(子コンポーネント)から受けたデータをもとに「ジャンル」という値で絞って再表示させたいです。
※Firestoreのpostsコレクションのデータにはそれぞれgenreというフィールドに値は取得しております。

イメージ説明

this.postDataの中身は以下のようになっております。

イメージ説明

入力フォームで入力された値はpost.vue(子コンポーネント)より取得はできているので
あとは以下を行って表示させると思うのですが、どのように条件を絞ってあげたらいいのか分からない状況です。

  • valueを引数に渡しているので、valueをもとにデータを絞る
  • Firestore内のフィールド「genre」でデータを絞る
  • postData内を一度クリアしてあげて再pushしてあげる

分かる方いらっしゃいましたらお力添えをいただきたいです。

よろしくお願いいたします。

#post.vue(子コンポーネント)

<input placeholder="例)アクション 恋愛 ミステリー SF ホラー ミュージカル etc.." class="search-main-item" type="search" @input="searchData($event.target.value)"/>
searchData(value) { this.$emit("searchData", value); }

#board.vue(親コンポーネント)

<template> <div> <Header /> <Post @searchData="search" /> <!--子コンポーネントから$emitで受けたデータを関数とする。※searchに()を付けるとvalueの引数が取れないので注意。--> <div class="post"> <h2 id="top" class="post-tll neon">投稿一覧</h2> <div class="post-inner"> <div class="post-items"> <paginate name="paginate-log" tag="ol" :list="postData" :per="12"> <List v-for="(list, index) in paginated('paginate-log')" :index="index" :list="list" :userDatas="userDatas" :key="list.id" /> </paginate> <paginate-links for="paginate-log" class="pagination flex" v-scroll-to="postTop" :show-step-links="true" ></paginate-links> <!--postDataのデータをlist関数とindex関数にそれぞれ格納--> </div> </div> </div> </div> </template>
export default { data() { return { title: "", contents: "", image: "", postData: [], paginate: ["paginate-log"], postTop: "#top", userDatas: [], }; }, components: { Header, Post, List }, methods: { search(value) { firebase .firestore() .collection("posts") .orderBy("genre") .orderBy("time", "desc") .get() .then(snapshot => { snapshot.forEach(doc => { this.postData.push({ ...doc.data(), id: doc.id }); }); }); } },

#追記1

以下はコードになります。

イメージ説明

実際にタイプした時の画面になります。
入力したタイミングでリアルタイムで表示が切り替われば理想です。。

イメージ説明

#追記2
以下のようにコードを書き替えましたので掲載させていただきます。

#board.vue(親コンポーネント)

html

1<template> 2 <div> 3 <Header /> 4 <Post v-model="searchWord" /> 5 <div class="post"> 6 <h2 id="top" class="post-tll neon">投稿一覧</h2> 7 <div class="post-inner"> 8 <div class="post-items"> 9 <paginate name="paginate-log" tag="ol" :list="filteredPostData" :per="12"> 10 <List 11 v-for="(list, index) in paginated('paginate-log')" 12 :index="index" 13 :list="list" 14 :userDatas="userDatas" 15 :key="list.id" 16 /> 17 </paginate> 18 <paginate-links 19 for="paginate-log" 20 class="pagination flex" 21 v-scroll-to="postTop" 22 :show-step-links="true" 23 ></paginate-links> 24 </div> 25 </div> 26 </div> 27 </div> 28</template>

js

1export default { 2 data() { 3 return { 4 title: "", 5 contents: "", 6 image: "", 7 postData: [], 8 paginate: ["paginate-log"], 9 postTop: "#top", 10 userDatas: [], 11 searchWord: "", 12 filteredPostData: [] 13 }; 14 }, 15 components: { 16 Header, 17 Post, 18 List 19 }, 20 computed: { 21 filteredPostData(value) { 22 console.log(value); 23 if (value != "") { 24 return this.postData.filter(v => { 25 return ~v.genre.indexOf(this.searchWord); 26 }); 27 } else { 28 return this.postData; 29 } 30 } 31 } 32 }, 33 created() { 34 // "posts"コレクションの全ドキュメントを取得。 35 firebase 36 .firestore() 37 .collection("posts") 38 .orderBy("time", "desc") 39 .get() 40 .then(snapshot => { 41 //"posts"(参照先)のスナップショットを得る 42 snapshot.forEach(doc => { 43 //上記で得たデータをforEachでドキュメントの数だけ"doc"データに格納 44 this.postData.push({ ...doc.data(), id: doc.id }); 45 //更にpostDataの空配列に格納した"doc"データを格納 46 }); 47 }); 48}; 49</script> 50

#post.vue(子コンポーネント)

html

1<div class="search-inner flex"> 2 <h2 class="search-tll neon flex">Cinemaryを検索する</h2> 3 <hr class="separate" /> 4 <div class="search-main-contens flex"> 5 <input 6 placeholder="例)アクション 恋愛 ミステリー SF ホラー ミュージカル etc.." 7 class="search-main-item" 8 type="search" 9 v-model="inputValue" 10 /> 11 </div>

js

1export default { 2 data() { 3 return { 4 5 ~ 省略 ~ 6 7}, 8 props: { 9 value: { 10 type: String, 11 required: true 12 } 13 }, 14 computed: { 15 inputValue: { 16 get() { 17 return this.value; 18 }, 19 set(value) { 20 this.$emit("input", value); 21 } 22 } 23 }, 24 searchData(value) { 25 this.$emit("searchData", value); 26 } 27 } 28}; 29

イメージ説明

また、以下のようなエラーが出ておりまして調べてみましたが赤で囲った箇所のエラー内容で
objectとなっている原因が分からなかった為、エラー内容を追記いたします・・

[Vue warn]: Method "computed" has type "object" in the component definition. Did you reference the function correctly?

メソッド "computed" は、コンポーネント定義のタイプ "object" を持っています。関数を正しく参照しましたか?


青色括弧につきましては、 filteredPostDataをdata()内で初期化してくださいとのことでしたので、
data()内で filteredPostData: []と初期化しました。

#追記3

イメージ説明

#追記4

###検索前

イメージ説明

###検索後

イメージ説明

###「t」を削除後

イメージ説明

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

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

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

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

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

guest

回答1

0

ベストアンサー

現状の@inputで発火するやり方ですと、
一文字一文字タイプするたびにfirestoreへのアクセスが発生してしまうので、
"初期読み込み時にpostData"が取得されているのであれば、下記のような形でいかがでしょうか?

methods: { search (value) { if (value != '') { this.postData = this.postData.filter( (v) => return ~v.genre.indexOf(value) ) } } },

投稿2021/06/05 22:20

m2l

総合スコア318

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

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

TMTN

2021/06/06 06:10

m2l様 こちらの質問に対してもご回答ありがとうございます。 返信遅くなり申し訳ございません・・ お力添え本当に助かります。 初期読み込み時に既にスクショを掲載させて頂いておりますように投稿一覧として表示はしておりますので、 m2l様の頂いたコードを参考に以下のようにしました。 少し括弧の位置に不備があったのかエラーが出てしまっていたため、一部修正して以下のようにしてます。 ------------------------------------------------------------- methods: { search(value) { if (value != "") { this.postData = this.postData.filter(v => { return ~v.genre.indexOf(value); }); } } }, ------------------------------------------------------- しかし、現状、input内に1文字でも入力がされると追記させて頂いたスクショのように表示がされなくなってしまいました。。 そもそもinputではm2l様の仰る通り1文字1文字するたびにfirestoreへのアクセスが発生してしまうのであまり適していないのでしょうか。。セレクトボックスのが使い勝手良いのかなとも考えたりします。 ※可能であればinputで実装したいですが・・ 追記に詳細追加いたしますのでご確認お願いいたします。
m2l

2021/06/06 07:46

ご連携いただきまして、ありがとうございます。 検索形式はお任せでよいですが、データ自体はどこかに退避して行った方が良いと思います。 (firestoreのアクセスにも制限があったかとおもいますし、有料プランの場合その分だけ費用が掛かってしまいますので。) 少し修正量多くなってしまいますが、computedに移して行っていった方が良さそうなので下記ですといかがでしょうか? 抜けがございましたら申し訳ございませんが宜しくお願い致します。 --- #1 v-modelに変更 <Post v-model="searchWord" /> #2 data部にsearchWord追加 searchWord: "", #3 post.vue の template部 をv-model に変更 <input placeholder="例)アクション 恋愛 ミステリー SF ホラー ミュージカル etc.." class="search-main-item" type="search" v-model="inputValue"/> #4 post.vue の propsを下記にする props: { value: { type: String, required: true } } #5 post.vue の computed に getterとsetterを追加 computed: { inputValue: { get() { return this.value; }, set(value) { this.$emit("input", value); } } } #6 postDataをcomputed経由に変更 <paginate name="paginate-log" tag="ol" :list="filteredPostData" :per="12"> #7 computedを追加し、サーチ内容がない場合はそのまま、それ以外はフィルタ結果を返す computed: { filteredPostData () { if (value != "") { return this.postData.filter(v => { return ~v.genre.indexOf(this.searchWord); }); } else { return this.postData } } } ---
TMTN

2021/06/06 08:21 編集

m2l様 ご回答ありがとうございます。 検索形式は入力フォーム形式で大丈夫であればこのままinputタグで進めてみます。 ありがとうございます。 確かにm2l様の仰る通りpostDataをクリアにして行うより一度データ自体はどこかに退避して 別々で行ったほうが良さそうですね。 今から仕事の為、実際の環境で確かめられるのは明日になります・・。 沢山修正頂きご提示頂きましたがすぐに確かめれずすいません。 明日帰宅後確認して改めてご連絡させて致します。
m2l

2021/06/06 08:24

ご返信頂きましてありがとうございます。 かしこまりました! 何卒宜しくお願いします。
TMTN

2021/06/06 09:41 編集

m2l様 今確かめられる環境でない為、ご提示頂いたコードを見てのご質問になるのですが、2点ございます。 ⑴#4 post.vue の propsを下記にする、について propsで親コンポーネントから子コンポーネントへデータを渡す場合、 親コンポーネント側で子コンポーネントに対して以下のようにしてあげる必要があるとの認識でしたが必要はなかったでしょうか。 ------------------------------------------------------------- <Post v-model="searchWord" :value="value" /> ------------------------------------------------------------- ⑵#5 post.vue の computed に getterとsetterを追加のset(value)について ------------------------------------------------------------- set(value) { this.$emit("input", value); } ------------------------------------------------------------- this.$emit("input", value);の$emit()内の第一引数はカスタムイベント名が入る認識ですが、 template部 を@inputからv-model に変更にしている為、inputというイベントがなく カスタムイベントが必要なのかなとふと感じたのですが、必要なかったでしょうか。 以上2点になりますが、コードを見ての質問なので自身の認識誤りでしたらすいません・・
TMTN

2021/06/06 10:17

m2l様 ご回答ありがとうございます。 確かに記事を読む限りではgetter, setterのような形では問題なさそうですね。 ご確認頂きありがとうございました。
TMTN

2021/06/07 02:55

m2l様 大変お待たせ致しました。 ご確認遅くなり申し訳ございません。 追記2として内容を追加させていただきました。 ------------------------------------------------------------------ computed: { filteredPostData(value) { console.log(value); if (value != "") { return this.postData.filter(v => { return ~v.genre.indexOf(this.searchWord); }); } else { return this.postData; } } } ------------------------------------------------------------------ valueに対してエラーが出ておりましたので、filteredPostData(value)として引数に渡しました。 しかし、コンソールでvalueの値を子コンポーネントから渡ってきているか確認をしてみたのですが、 コンソールの結果がデバックツール内で表示されなく、渡ってきていないような気もします・・ 追記2にboard.vue(親コンポーネント)とpost.vue(子コンポーネント)を全て追記しましたのでお時間ある時にご確認いただけると幸いです・・。よろしくお願いいたします。
m2l

2021/06/07 10:28

仕事でご返信遅くなり申し訳ございません。。 すみません。。 value引数に入れてありましたが、こちらのミスなので下記でどうでしょうか? (机上でしか確認できず、申し訳ないです。) ------------------------------------------------------------------ computed: { filteredPostData() { if (this.searchWord != "") { return this.postData.filter(v => { return ~v.genre.indexOf(this.searchWord); }); } else { return this.postData; } } } ------------------------------------------------------------------
TMTN

2021/06/07 11:02

とんでもございません!お仕事お疲れのところ回答ありがとうございます・・ 提示いただいたコードに修正いたしましたが、追記3のようにまだエラーが出てしまっておりますね・・
m2l

2021/06/07 11:17

ご返信ありがとうございます。 filteredPostDataはdataの中に入れなくても大丈夫ですね。 Computedのところ、methodsの中に入っていたりしないでしょうか?
TMTN

2021/06/07 11:32

m2l様 ご確認いただきありがとうございます。 仰る通り誤ってmethodsの中にComputedが入っておりました・・ 大変申し訳ございませんでした・・ しかし、修正しましたが追記4にあるように検索前では全ての投稿が表示(postData)、 検索フォームに「t」と試しに入力したところ検索後のように表示されませんでした。。 また、「t」を消してinputタグ内に文字が入っていない状態にしても全ての投稿が表示(postData)に戻らない状態です・・リロードしたら戻ることを確認出来ております。
m2l

2021/06/07 11:43

かしこまりました。 そうなんですね。。 後程codepenでためしてみますのでお待ち下さいませ。
TMTN

2021/06/07 11:45

お仕事後でお疲れのところ本当に申し訳ございません・・ 本当にありがとうございます・・ お時間余裕ある時で構いませんのでよろしくお願いいたします。
m2l

2021/06/07 12:43

少し親子のコンポーネント間でリアルタイム検索できるか試してみましたが、特に問題なくできそうな形でした。 子コンポーネントに不要なメソッドや、computedがmethodsの中に入っている等ございませんでしょうか? https://codepen.io/masanori0209/pen/RwpJVrj
TMTN

2021/06/07 13:10

m2l様 確認が遅くなっていて申し訳ございません・・ m2l様のご確認いただいたcodepenでは確かにリアルタイム検索できておりますね。。 いまだコードを見比べ確認しておりますが、原因が分からずでして・・もう少々確認にお時間いただきますようお願いいたします・・
TMTN

2021/06/07 13:25

m2l様 大変遅くなりました。 私の検索仕方に誤りがありました・・ デベロッパーツールのsauceで中身を確認していると「return ~v.genre.indexOf(this.searchWord);」と genreで検索ワードとしているのに追記に掲載したようにgenreには存在しない「t」という値で入力していました・・本当に恥ずかしい間違いでm2l様のお時間を頂戴してしまい申し訳ございません・・ genreで検索かけるとちゃんとリアルタイムで表示してくれました・・今回もお忙しい中、またお仕事でお疲れの中ご教示いただきまして本当にありがとうございました!!感謝の気持ちでいっぱいです・・
m2l

2021/06/07 13:29

いえいえ!うまく解決できて何よりでございます。
TMTN

2021/06/07 13:41

毎度ご回答いただいた際、色々と確認ややり取りでお時間頂戴してしまい申し訳ございません・・
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問