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

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

ただいまの
回答率

87.92%

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

受付中

回答 1

投稿 編集

flag 質問者が9時間前に「まだ回答を求めています」と言っています。

  • 評価
  • クリップ 0
  • VIEW 105

score 40

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

現状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内に保存してあげたらよいのか悩んでいる状況です。

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/06/19 01:06

    m2l様

    お世話になっております。

    本件、データ構造から少し複雑になっていて分かりにくいと指摘があったため、アドバイスを頂きソースを変更させて頂きました。やりたい事は変わらずなのですが、bookmark.vueでの投稿の取得の仕方や、投稿一覧(board.vue)でもブックマーク保存と削除できるようにデータ構造を変えたり変更させて頂きました・・

    しかし、自身では行き詰まってしまい実現したい事わからない事を追記に書かせて頂きました・・

    もしお時間ある時ご教示頂けないでしょうか・・
    自身で調べ試行錯誤して実装を試みたのですがソースを変えたことでなかなか
    正常に実装できていたことも上手くいかなくて・・行き詰まってしまい。。
    本当に申し訳御座いませんが、どうかm2l様のお力添えを頂きたいです・・

    キャンセル

  • 2021/06/19 20:38

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

    キャンセル

  • 2021/06/19 21:04 編集

    m2l様

    とんでもございません。
    土日の休日にお時間割いてご協力いただき本当に申し訳御座いません・・

    ただいま少し改めて自身でソースを少しいじったため、改めてgithubにpushいたしました。
    また記事の内容につきましても⑴を内容変更いたしました。

    ご確認お願いいたします。。

    キャンセル

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

  • ただいまの回答率 87.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る