skip to Main Content

I’m working on a vuejs/(vuex) for state management/firebase project of posts.

So I have a firestore collection of posts (array of objects that have name id owner content and timestamp for creation …)

I’m retrieving that data by using the onSnapshot methode and it’s stored on blogPosts variable… and we show theme all, when a user want to visit a single post it will redirect him to the route of the single post (…./view-post/postid) and i filter that array using the id of the post to have an array of one element (which is the post he visited)

when the filter complete i got all the data and i fill theme on the template like these

<template>
    <NavBarView />
    <section class="preview" v-if="currentBlog">
        <h2>{{ currentBlog[0].blogTitle }}</h2>
        <p>Posted on: <span>{{ dateFormat() }}</span> <span v-if="currentBlog[0].editTime">(Last edit:
                {{ editFormat() }})</span></p>
        <img class="blogCover" :src="currentBlog[0].blogCoverFileURL" :alt="currentBlog[0].blogCoverPhoto">
        <div class="html-blog" v-html="currentBlog[0].blogHTML"></div>
        <hr />
    </section>
</template>
<script>
    export default {
        data() {
            return {
                currentBlog: null
            }
        },
        mounted: {
            this.currentBlog = this.blogPosts.filter((post) => {
                return post.blogID == this.$route.params.id
            })
        },
        computed: {
            ...mapState(['blogPosts'])
        }
//note that i have exported all the requirements such as mapState and firebase functions ... didn't write theme here
    } 
</script>

now the problem is that the filter is occurring before the data are fetched from firebase and i can’t handle that so it’s always returning can’t read property of null (currentBlog[0])

i found a solution which is sending a getDoc request to firebase but it’s a bad idea, (why i send request and i have all the posts here so i can filter it directly and get the specific post) which didn’t work!!!!!

any solution??

2

Answers


  1. I would suggest that rather than trying to do this operation once on mounted that you instead re-run the filter every time that state.blogPosts is updated. You can easily do this through a computed property. See below:

    export default {
            data() {
                return {
                }
            },
            computed: {
                ...mapState(['blogPosts']),
                currentBlog(){
                  const posts = this.$store.state.blogPosts;
                  if(Array.isArray(posts)) {
                      return posts.filter((post) => {
                         return post.blogID == this.$route.params.id
                      });
                   } else {
                      return null
                   }
                } 
           }
        } 
    
    Login or Signup to reply.
  2. Looking at your template, I’m under the impression currentBlog should not be an array, as you don’t ever have more than 1 element in that array (you’re filtering by blogID, which I’m guessing is a unique identifier). You need to .find() the blog entry, not .filter() it:

      computed: {
        blogID() {
          return this.$route.params.id;
        },
        currentBlog() {
          return this.$store.state.blogPosts.find((b) => b.blogID === this.blogID);
        }
      }
    

    Note: if state.blogPosts does not start as an empty array (as it should), you might want to use:

        currentBlog() {
          return (this.$store.state.blogPosts || []).find(
            (b) => b.blogID === this.blogID
          );
        }
    

    And now replace all currentBlog[0]s with currentBlog:

    <template>
      <NavBarView />
      <section class="preview" v-if="currentBlog">
        <h2>{{ currentBlog.blogTitle }}</h2>
        <p>
          Posted on: <span>{{ dateFormat() }}</span>
          <span v-if="currentBlog.editTime">(Last edit: {{ editFormat() }})</span>
        </p>
        <img
          class="blogCover"
          :src="currentBlog.blogCoverFileURL"
          :alt="currentBlog.blogCoverPhoto"
        />
        <div class="html-blog" v-html="currentBlog.blogHTML"></div>
        <hr />
      </section>
    </template>
    

    Important: make sure in every single method/computed/watch in the script part of the component, if using currentBlog, you’re first checking if it’s not falsy. Generic example:

      computed: {
        blogHTML() {
          return this.currentBlog?.blogHTML
          /* shorthand for:
           * return this.currentBlog && currentBlog.blogHTML
           */
        }
      }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search