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

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

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

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

Firebase

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

Cloud Firestore

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

Q&A

解決済

1回答

1736閲覧

【Vue × Firestore】ブックマーク機能にて「保存」と「削除」と条件に応じて切り替えたい

TMTN

総合スコア53

Vue.js

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

Firebase

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

Cloud Firestore

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

0グッド

0クリップ

投稿2021/06/10 11:45

編集2021/06/20 06:04

#ブックマーク機能にて「保存」と「削除」と条件に応じて切り替えたい

現状list.vueにて以下のように左下のボタンを用意して、それぞれsaveBookmark()とdeleteBookmark()を作成し、並べている状態です。

イメージ説明

#実際のソース

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

<List v-for="(list) in paginated('paginate-bookmarkList')" :list="list" :key="list.id" /> <!-- data内にあるbookmarkList配列から、v-forディレクティブを使って1つずつ要素を取り出し描画。 -->
export default { data() { return { profileData: {}, paginate: ["paginate-bookmarkList"], bookmarkList: [], currentUserBookmarkIds: [], uid: null }; }, components: { Header, List }, created() { firebase .firestore() .collection("users") .doc(this.$route.params.uid) .get() .then(snapshot => { this.profileData = snapshot.data(); }); //各ユーザー(ページ内のユーザー)がブックマークしたリスト firebase .firestore() .collection("users") .doc(this.$route.params.uid) .collection("bookmarks") .orderBy("time", "desc") .get() .then(snapshot => { snapshot.forEach(doc => { this.bookmarkList.push(doc.data()); }); }); //ログイン中ユーザーがブックマークしたリスト firebase.auth().onAuthStateChanged(user => { const uid = user.uid; firebase .firestore() .collection("users") //「users」コレクションを参照 .doc(uid) //ログイン中のユーザーを参照 .collection("bookmarks") //「bookmarks」サブコレクションを参照 .get() .then(snapshot => { snapshot.forEach(doc => { //forEachで全てのドキュメントに対して this.currentUserBookmarkIds.push(doc.data().postId); //「postId」を追加し、this.currentUserBookmarkIdsへ格納 }); }); firebase .firestore() .collection("posts") //「posts」コレクションを参照 .get() .then(snapshot => { snapshot.forEach(doc => { //forEachで全てのドキュメントに対して if (this.currentUserBookmarkIds.includes(doc.data().id)) { //this.currentUserBookmarkIdsに「id」が含まれていたら、 this.bookmarkList.push({ ...doc.data(), isBookmarked: true }); console.log(this.bookmarkList); //...doc.data()としてバラした「posts」の投稿データとisBookmarkedを代入。 } }); }); }); } };

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

<template> <div class="list"> <div class="face face1 flex"> <div class="content"> <img class="profile-icon" width="50" height="50" :src=" returnUserData() ? returnUserData().uploadedImage.fileUrl : preview " /> <h3>{{ list.title }}</h3> </div> </div> <div class="face face2 flex"> <div class="content flex"> <button class="hide-btn" @click="deletePost">×</button> <p>{{ list.description }}</p> <router-link :to="`/chat/${list.id}`" class="join-btn flex">ルームへ参加</router-link> <!-- 「list.id」propsで親コンポーネントから取得したidを取得。--> <img src="../assets/ブックマーク保存.jpg" alt="ブックマーク" class="bookmark-icon" @click="saveBookmark"                     v-if="hasBookmark(list)" /> <img src="../assets/ブックマーク未保存.jpg" alt="ブックマーク" class="bookmark-icon" @click="deleteBookmark"                     v-else /> <p class="post-time">{{ list.time.toDate().toLocaleString() }}</p> </div> </div> </div> </template>
export default { data() { return { bookmarkId: "", userDatas: [], preview: require("../assets/デフォルト画像.jpg") }; }, props: { list: { type: Object }, index: { type: Number }, }, methods: { ~ 省略 ~ hasBookmark(book) { // ブックマークリスト内にbook idがあればtrue それ以外はfalse return this.bookmark.some(value => value.id === book.id); }, saveBookmark() { const id = firebase .firestore() .collection("users") .doc(this.$route.params.uid) .collection("bookmarks") .doc().id; firebase .firestore() .collection("users") //「users」コレクションを参照 .doc(this.$route.params.uid) //対象ページのユーザーを参照 .collection("bookmarks") //「bookmarks」サブコレクションを参照 .doc(id) //自動生成されたドキュメントIDを参照 .set( { postId: this.list.id, //「postId」に投稿データである「this.list.id」を代入。 time: firebase.firestore.FieldValue.serverTimestamp() } ) .then(() => { this.$swal("ブックマークに追加しました。", { icon: "success" }); }) .catch(() => { this.$swal("ブックマークを追加出来ません。", { icon: "error" }); }); }, deleteBookmark() { firebase .firestore() .collection("users") .doc(this.$route.params.uid) .collection("bookmarks") .doc(this.list.postId) .delete() .then(() => { this.$swal("ブックマークを取り消ししました。", { icon: "success" }); this.$router.go({ path: `/bookmark/${this.$route.params.uid}`, force: true }); }) .catch(() => { this.$swal("ブックマークを取り消し出来ません。", { icon: "error" }); }); },

#追記 「内容整理」

####⑴bookmark.vueにてブックマークされた一覧を表示する際について。
ブックマーク保存(savebookmark)する際、投稿(list)のidをサブコレクションのbookmarksへ「postId」としてデータを保存してあげて、
そのブックマークされた際に取得した「postId」と投稿時に取得する「id」が一致されたものをブックマーク一覧に表示させています。

◆投稿時に取得する「id」
イメージ説明
◆ブックマークされた際に取得した「postId」
イメージ説明

しかし、以下2点でthis.bookmarkListに追加しているからなのか、一つの投稿をブックマークすると2つの投稿が表示されてしまいます。

####各ユーザー(ページ内のユーザー)がブックマークしたリスト

firebase .firestore() .collection("users") .doc(this.$route.params.uid) .collection("bookmarks") .orderBy("time", "desc") .get() .then(snapshot => { snapshot.forEach(doc => { this.bookmarkList.push(doc.data()); }); });

####ログイン中ユーザーがブックマークしたリスト

firebase.auth().onAuthStateChanged(user => { const uid = user.uid; firebase .firestore() .collection("users") //「users」コレクションを参照 .doc(uid) //ログイン中のユーザーを参照 .collection("bookmarks") //「bookmarks」サブコレクションを参照 .get() .then(snapshot => { snapshot.forEach(doc => { //forEachで全てのドキュメントに対して this.currentUserBookmarkIds.push(doc.data().postId); //「postId」を追加し、this.currentUserBookmarkIdsへ格納 }); }); firebase .firestore() .collection("posts") //「posts」コレクションを参照 .get() .then(snapshot => { snapshot.forEach(doc => { //forEachで全てのドキュメントに対して if (this.currentUserBookmarkIds.includes(doc.data().id)) { //this.currentUserBookmarkIdsに「id」が含まれていたら、 this.bookmarkList.push({ ...doc.data(), isBookmarked: true }); console.log(this.bookmarkList); //...doc.data()としてバラした「posts」の投稿データとisBookmarkedを代入。 } }); }); }); }

単純に「各ユーザー(ページ内のユーザー)がブックマークしたリスト」を消したらいいのかと思ったのですが、消すと他のユーザーのブックマーク一覧を閲覧した際にそのユーザーのブックマーク一覧を表示するのではなく、ログイン中のユーザーのブックマーク一覧を表示してしまいます・・

他のユーザーのブックマークを閲覧した際にはそのユーザーのブックマーク一覧を表示して、被らずに1枚のみを表示したいです。

イメージ説明

#⑵投稿の中にisBookmarkedとしてブックマークされている投稿かどうかが判定したい。

※以下赤で囲っている箇所参照

イメージ説明

ブックマークされている投稿かどうかが判定できる情報「isBookmarked」を一緒に渡してあげているので、listコンポーネント側で、isBookmarkedがtrue,かfalseかで表示を変えてあげようと考えておりますが、以下について悩んでおります。

propsで子コンポーネントであるlist.vueにデータを渡し、「isBookmarked」がtrue,かfalseか確認すればよろしいのでしょうか。
もしくはFirestore内に「isBookmarked」を保存してあげたほうがよいのでしょうか。

またlist.vue側でどのように「isBookmarked」がtrue,かfalseか判定してあげたらよいのか分からない状況です・・

#⑶board.vue「投稿一覧」からでもブックマークの「保存」と「削除」を行えるようにしたい

board.vueコンポーネントでも同様に、ログインしているユーザーのブックマーク一覧から、それに紐づいたpostIdを持つ投稿を取り出してきて、それにisBookmarkedをつけてあげてboard.vue側でもブックマークの保存と削除を行えるようにしたいと考えております。

export default { data() { return { currentUserBookmarkIds: [], uid: null }; }, //各ユーザーがブックマークしたリスト firebase .firestore() .collection("users") //「users」コレクションを参照 .doc(this.$route.params.uid) //現在表示中ユーザーを参照 .collection("bookmarks") //「bookmarks」サブコレクションを参照 .get() .then(snapshot => { snapshot.forEach(doc => { //forEachで全てのドキュメントに対して this.currentUserBookmarkIds.push(doc.data().postId); //「postId」を追加し、this.currentUserBookmarkIdsへ格納 }); }); firebase .firestore() .collection("posts") //「posts」コレクションを参照 .orderBy("time", "desc") .get() .then(snapshot => { snapshot.forEach(doc => { //forEachで全てのドキュメントに対して if (this.currentUserBookmarkIds.includes(doc.data().id)) { //this.currentUserBookmarkIdsに「id」が含まれていたら、 this.postData.push({ isBookmarked: true }); //isBookmarkedを追加。 } }); }); }

以上bookmark.vueと同様の方法でisBookmarked: trueをpushしてあげようと考えていますが、こちらにつきましても⑵同様にpropsでデータを渡すかFirestore内に保存してあげたらよいのか悩んでいる状況です。

ご教示頂けると幸いです。。

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

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

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

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

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

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

guest

回答1

0

ベストアンサー

回答が分かりづらかったら申し訳ございませんが、
bookmarksをページ読み込み時に取得しておきまして、bookmarksと、bookが一致しているかどうかをv-ifなどで切り替えることで可能です。

投稿2021/06/10 15:14

m2l

総合スコア318

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

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

TMTN

2021/06/10 15:22

m2l様 お世話になっております。 ご回答ありがとうございます。 大変申し訳ございませんが、bookmarksをページ読み込み時に取得し、bookmarksと、bookが一致しているかどうかとはどういうことでしょうか・・分からなくて申し訳ないです・・
m2l

2021/06/10 15:27

bookmarksを読み込んだ際にdataに格納いたしまして、 v-if="hasBookmark(book)" などで要素ごと条件分岐しておきます。 methodsでhasBookmark(book)を定義しつつbookmarksのリストをsome関数などを用いてtrue or falseで返す事で上手くできるとは思うのですが、いかがでしょう?
TMTN

2021/06/10 15:38

m2l様 saveBookmark()とdeleteBookmarks()のそれぞれのメソッドにてbookmarksを読み込んだ際に 何をdata内に格納するのでしょうか・・ hasBookmark(book)というメソッドを作成して、コレクションのbookmaerksを参照してからpushでdata内に追加するのでしょうか・・ 質問ばかり申し訳ございません・・
m2l

2021/06/10 15:48

説明が下手でご迷惑をおかけいたします。 現状、下記の処理でbookmarkの一覧を取得していると思います。 --- firebase .firestore() .collection("users") .doc(this.$route.params.uid) .collection("bookmarks") .orderBy("time", "desc") .get() .then(snapshot => { snapshot.forEach(doc => { this.bookmarkList.push(doc.data()); }); }); --- List.vueのほうで実際にBookMarkをつけたり外したりするかと思うので そこで分岐する形になります。 --- <img src="../assets/ブックマーク保存.jpg" alt="ブックマーク" class="bookmark-icon" @click="saveBookmark" v-if="hasBookmark(list)"/> <img src="../assets/ブックマーク未保存.jpg" alt="ブックマーク" class="bookmark-icon" @click="deleteBookmark" v-else /> ---- 最後にmethodsでbook.idとbookmarkのidを比べる形です。 --- hasBookmark(book) { // bookmarkListはpropsなどで持ってきてください // ブックマークリスト内にbook idがあればtrue それ以外はfalse return this.bookmarkList.some(value => value.id === book.id) }
TMTN

2021/06/10 16:15

m2l様 分からない為に詳しくご教示いただき本当にありがとうございます・・ propsの時点で何故か今上手く親コンポーネント(bookmark.vue)から子コンポーネント(list.vue)へ変数が親から渡されていないというエラーが出てしまいます・・ 追記1に追加させていただきましたが、何が原因か分からなく・・ またbookmarkのidとは、追記1に追加しましたbookmarksコレクションに格納しているbookmarkIdのことかと思われますが、book idとはどこのidのことでしょうか。。
m2l

2021/06/10 21:33

list.vueは他の親コンポーネントでも使われている形でしょうか? 使いまわしているコンポーネントであればpropにdefault付与してあげると良いと思います。 bookmarkのidは保存した際のbook(list)のidを指しています。 実現したいこととしてはbook一覧上からユーザがブックマークしているかどうか判定してブックマーク済であれば外す処理ができるボタン、未済であればブックマークできるボタンと切り替えたいという認識ですが、そうではなさそうですか?
TMTN

2021/06/10 23:21

m2l様 おはようございます。 list.vueはおっしゃる通り他のコンポーネントでも使用して使い回しております。 (投稿一覧、自身の投稿した一覧、ブックマークした一覧) 使い回す際にはdefault値設定してあげないといけないのですね。 調べて記事を参考に以下のように設定してみましたが、こういうことでしょうか・・ ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー bookmark: { type: Object, required: true, default:() => {}, } ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー しかし、現状エラーコードとして追記したようにいまだエラーは解消されていない状況です・・ 追記に再度訂正したコードを掲載させて頂きました。 bookmarkのidは保存した際のbook(list)のidのことでしたか。。 現状保存する際、bookmarkIdしか取得しておりませんが、投稿のid「book(list)id」も別で取得してデータを格納してあげないといけないでしょうか・・? 実現したいことはm2l様のおっしゃる通りで間違い無いです。 自身の知識不足で質問ばかりでご迷惑お掛けして申し訳ございません・・
m2l

2021/06/11 10:07

default:[]とかでいけますでしょうか? ブックマークのほうは作りから見直したほうが良さそうな気がします。 ブックマークリストは基本的にユーザがブックマークしたものをリスト化したものなので、削除処理だけで問題ないかと思っており、 問題は本の一覧からブックマークをする場合にその本がブックマークしたかどうかは表示すると思いますのでその際にブックマーク済か否か判定する処理が必要になります。 その際には本の情報がないと判定ができませんので、保存する際に本の情報を保持するか外部キーのような形で保存する必要がございますね。
TMTN

2021/06/11 12:20

m2l様 ご連絡頂きありがとうございます。 現在仕事中になりますのでまた明日帰宅訂正しご確認致します。 bookmark.vueにつきましては見直したほうが良さそうなのですね・・ 現在作成しているように保存と削除をそれぞれ作るのではなく、削除処理だけで良いという事でしょうか。 本の一覧からブックマークをする場合にその本がブックマークしたかどうかは表示につきましては、ブックマーク保存アイコンとブックマーク削除アイコンの2つを用意して切替で考えておりましたが、ブックマーク済か否か判定する処理は必要になりますよね。。bookmarkIdとは別に保存する際に本の情報を保存してあげないといけないのですね。
TMTN

2021/06/12 02:38

m2l様 今帰宅し確認致しました。 propsの件、default:[]に変更して確認してみましたが以下のようなエラーが出てしまいました・・ ------------------------------------------------------ Type of the default value for 'bookmark' prop must be a function ------------------------------------------------------ 'bookmark'propのデフォルト値のタイプは関数である必要があります。
m2l

2021/06/12 02:42 編集

ファクトリ用意しないと行けないみたいですね。 下記でどうでしょう? default: () => ([])
TMTN

2021/06/12 02:57

ごめんなさい。そちらも調べてファクトリ関数にしないといけないらしく 確かめてみましたが同じ結果で以下のエラーが出る状況です・・ ------------------------------------------------------ [Vue warn]: Missing required prop: "bookmark" ------------------------------------------------------
m2l

2021/06/12 03:00

そうなんですね。。 となりますと他のコンポーネントにもきちんとprop定義するか、 required:falseで回避する形でいけますでしょうか?
TMTN

2021/06/12 03:19

一度required:trueとしていたものを削除してみると以下のエラーが検出されたため object型にしていたものをarray型にしたらエラーは出なくなりました。 ------------------------------------------------------- [Vue warn]: Invalid prop: type check failed for prop "bookmark". Expected Object, got Array ------------------------------------------------------- しかし、試しにテンプレート内で {{bookmark}}としてbookmarkの中身を確認してみたのですが特に何も表示されないです・・
m2l

2021/06/12 03:28

そうなんですね。 全体的に見てみないとちょっとわからない部分もあり、差し支えなければソースコードとかってgithubに上げられたりしてますか?(apikeyとかは隠した状態であげていただけますと助かります)
TMTN

2021/06/12 03:43 編集

お手数お掛けして申し訳ございません・・ 下記にてお送りさせて頂きますのでお時間ある時にでもご確認頂けると幸いです。。 https://github.com/TOMOYATANI/cinemary-app
m2l

2021/06/12 03:48

かしこまりました! ちょっとあまり上手く回答できずお時間かけてしまっているので全体的にソース見てみますね!
TMTN

2021/06/12 03:55

とんでもございません・・ 私こそ知識の浅さから丁寧に教えてくださってるのにも関わらず理解ができず申し訳ございません・・ よろしくお願いいたします。 ちなみに{{bookmark}}が表示されないと言いましたがbookmark.vueにのみ表示が確認されました・・! 見逃し申し訳ございません。。
TMTN

2021/06/12 16:13 編集

m2l様 夜分遅くに申し訳ございません。 propsの件、とりあえずエラーにつきましてはm2l様のご指摘いただいた通り修正すると解消し、 {{bookmark}}と中身もbookmark.vueにて取得できていることを確認でき、 あとは本題のブックマーク処理を調べて自身で試みているのですが一向に進まずでして・・。 図々しく本当に申し訳ございませんが、ブックマークの作り方から不適切な部分であったり その他もしアドバイス等あればご指摘頂きたく思います・・
TMTN

2021/06/14 08:55

m2l様 お世話になっております。 表題のブックマーク機能について自身で調べて試行錯誤して試みているのですが、どうしても上手く実装出来ずで行き詰まってしまっておりまして・・ 頼る宛もなくm2l様にご連絡させてただきました。 本当に恐縮なのですが、どうかお力添えを頂けませんでしょうか・・。 またgithubにつきまして他の方に確認頂く機会がほとんどなく、何か不足していて見れなかったりおかしな点がありましたらお申し付け下さい。 お忙しい中本当に申し訳御座いませんが、お時間ある時お返事お待ちしております。
m2l

2021/06/14 10:02

ご返信遅くなり申し訳ございません。 少し合間合間にはなると思いますが、こちらで見直したものをプルリクしますので、今しばらくお待ち下さいませ。 少し今週バタバタしていますので土日とかになってしまいますが大丈夫でしょうか?
TMTN

2021/06/14 10:08

m2l様 とんでもございません。 いえいえ、こちらこそお忙しい中ご連絡申し訳御座いません・・ 承知しました。m2l様の時間に余裕がある時で全然大丈夫です。 あとブックマーク機能追加できれば機能としては完成で、あとリファクタリングやレスポンシブやパフォーマンス改善等少々細かな点はありますのでそちらに取り掛かろうと考えております。 本当にありがとうございます・・助かります。。
TMTN

2021/06/18 16:06

m2l様 お世話になっております。 本件、データ構造から少し複雑になっていて分かりにくいと指摘があったため、アドバイスを頂きソースを変更させて頂きました。やりたい事は変わらずなのですが、bookmark.vueでの投稿の取得の仕方や、投稿一覧(board.vue)でもブックマーク保存と削除できるようにデータ構造を変えたり変更させて頂きました・・ しかし、自身では行き詰まってしまい実現したい事わからない事を追記に書かせて頂きました・・ もしお時間ある時ご教示頂けないでしょうか・・ 自身で調べ試行錯誤して実装を試みたのですがソースを変えたことでなかなか 正常に実装できていたことも上手くいかなくて・・行き詰まってしまい。。 本当に申し訳御座いませんが、どうかm2l様のお力添えを頂きたいです・・
m2l

2021/06/19 11:38

かしこまりました。 土日しか確認ができず、申し訳ございませんが Githubの方からこちらで解決方法プルリク挙げてみますね!
TMTN

2021/06/19 12:07 編集

m2l様 とんでもございません。 土日の休日にお時間割いてご協力いただき本当に申し訳御座いません・・ ただいま少し改めて自身でソースを少しいじったため、改めてgithubにpushいたしました。 また記事の内容につきましても⑴を内容変更いたしました。 ご確認お願いいたします。。
TMTN

2021/06/22 23:03

ブックマーク機能につきましては、なんとか自身で実装できました! お忙しい中お願いばかり大変失礼しました・・
TMTN

2021/06/23 00:56

m2l様 お世話になっております。 以下の質問で回答依頼を送れせていただいたのですが、 この1点で完成にしようと考えており、もしお時間あればで大丈夫ですので ご回答頂きたいと思いご連絡させて頂きました。。 https://teratail.com/questions/345568 もしわかればよろしくお願いいたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問