0

I can't pass data from a method to a prop. The method gets a field from a collection in firestore.

here is the parent compenent:

    <template>
          <post-card
            v-for="post in posts"
            :key="post.id"
            :userId="post.userId"
            :userName="userName(post.userId)"
          />
    </template>
    
    <script>
   components: {
      PostCard
}
    methods: {
         userName(id){
                this.$fire.firestore.collection('users').doc(id).get().then(snapshot => {
                  console.log("id", snapshot.data())
                  return snapshot.data().name
                })
              }
    }
    </script>

and here is the post card compenent:

<template>
{{userName}}
</template>
<script>
name: 'post-card'.
    props: [
      'userName',
      'userId'
    ],
</script>

can a prop get data this way? will this be reactive once working?

Teoman Kirac
  • 748
  • 3
  • 8
  • 16

1 Answers1

0

I don't have access to your specific Firestore data and mocking it would be quite difficult so I used JSONplaceholder for my example.

Here is how I would handle such case

index.vue

<template>
  <div>
    <post-card v-for="user in users" :key="user.id" :user="user" />
  </div>
</template>

<script>
export default {
  components: {
    'post-card': () => import('~/components/PostCard.vue'),
  },
  data() {
    return {
      users: [],
    }
  },
  async mounted() {
    this.users = await this.$axios.$get(
      'https://jsonplaceholder.typicode.com/users/'
    )
  },
  // methods: {
  //   async fetchUsername(id) {
  //     const snapshot = await this.$fire.firestore
  //       .collection('users')
  //       .doc(id)
  //       .get()
  //     console.log('id', snapshot.data())
  //     return snapshot.data().name
  //   },
  // },
}
</script>

PostCard.vue

<template>
  <div>
    <span>{{ user.name }}</span>
    <pre>{{ details }}</pre>
    <hr />
  </div>
</template>

<script>
export default {
  name: 'PostCard',
  props: {
    user: {
      type: Object,
      default: () => {},
    },
  },
  data() {
    return {
      details: {},
    }
  },
  async mounted() {
    this.details = await this.$axios.$get(
      `https://jsonplaceholder.typicode.com/users/${this.user.id}`
    )
  },
}
</script>

Here, I'm using this.$axios.$get but it's basically a fancy axios + .data combo, nothing special.


I didn't used :userName="userName(post.userId)" in the parent as you because this is not feasible per se. A template is synchronous in that case, meaning that you will not get post.userId when running your function because the request would still be pending. Meanwhile, your child needs this info straight to render himself.

In Vue, you usually do this kind of logic inside of the child component itself or in a mounted hook in pair with the collection fetch. This is mainly because of how a Vue child component mounts as explained here: https://stackoverflow.com/a/44319825/8816585

You could maybe use this kind of mounted trick but I don't recommend working in that way because it will be hacky, overall more difficult and unnecessary.

kissu
  • 40,416
  • 14
  • 65
  • 133