【问题标题】:Vue, firestore: how to display LIVE data after merging collectionsVue,firestore:合并集合后如何显示 LIVE 数据
【发布时间】:2021-01-04 18:22:41
【问题描述】:

见下方编辑


my last question 相比,我有了很大的进步,但经过几天的工作,我再次陷入困境。

将 Vue、Vue-router、Vuex 和 Vuetify 与 Google 上的数据一起使用可以 Firestore

我想实时更新我的​​数据,但我找不到执行此操作的方法。 我是否需要重组,例如将产品和类别转移到一个集合中? 或者是否有任何绑定或查询魔法来完成这项工作。 正如您在下面看到的,它可以很好地加载点击数据,但我需要实时绑定,因为您可以打开页面并且有人可以出售最后一块(amountLeft = 0)。 (还有很多未来的想法)。

我的数据结构如下:

categories: {
  cat_food: {
    name: 'Food'
    parentCat: 'nC'
  },
  cat_drinks: {
    name: 'Food'
    parentCat: 'nC'
  },
  cat_beer: {
    name: 'Beer'
    parentCat: 'cat_drinks'
  },
  cat_spritz: {
    name: 'Spritzer'
    parentCat: 'cat_drinks'
  },
}

products: {
  prod_mara: {
    name: 'Maracuja Spritzer'
    price: 1.5
    amountLeft: 9
    cat: ['cat_spritz']
  },
  prod_capp: {
    name: 'Cappuccino'
    price: 2
    cat: ['cat_drinks']
  },
}

类别和产品构成一棵树。 GIF 显示我打开类别以显示产品。当您有价格标签时,您会看到它是一种产品。 您可以看到有两个类别具有相同的父项 (cat_drinks)。 产品 prod_capp 也被分配到类别并与类别并排显示。

我目前是这样获取数据的:

catsOrProd.js

import { catsColl, productsColl } from '../firebase'

const state = {
  catOrProducts: [],
}

const mutations = {
  setCats(state, val) {
    state.catOrProducts = val
  }
}

const actions = {
  // https://vuefire.vuejs.org/api/vuexfire.html#firestoreaction

  async bindCatsWithProducts({ commit, dispatch }, CatID) {
    if (CatID) {
      // console.log('if CatID: ', CatID)
      await Promise.all([
        catsColl.where('parentCat', '==', CatID).orderBy('name', 'asc').get(),
        productsColl.where('cats', 'array-contains', CatID).orderBy('name', 'asc').get()
      ])
        .then(snap => dispatch('moveCatToArray', snap))
    } else {
      // console.log('else CatID: ', CatID)
      await Promise.all([
        catsColl.where('parentCat', '==', 'nC').orderBy('name', 'asc').get(),
        productsColl.where('cats', 'array-contains', 'nC').orderBy('name', 'asc').get()
      ])
        .then(snap => dispatch('moveCatToArray', snap))
    }
  },

  async moveCatToArray({ commit }, snap) {
    const catsArray = []
    // console.log(snap)
    await Promise.all([
      snap[0].forEach(cat => {
        catsArray.push({ id: cat.id, ...cat.data() })
      }),
      snap[1].forEach(cat => {
        catsArray.push({ id: cat.id, ...cat.data() })
      })
    ])
      .then(() => commit('setCats', catsArray))
  }
}

export default {
  namespaced: true,
  state,
  actions,
  mutations,
}

这是我的 vue 文件的一部分,它在屏幕上显示数据。我省略了不必要的部分。 要打开所有带有道具的路线,然后单击类别将路由器发送到下一个类别。 (这样我可以使用浏览器功能向后移动)。 Sale.vue

<template>
...........
<v-col
  v-for="catOrProduct in catOrProducts"
  :key="catOrProduct.id"
  @click.prevent="leftClickProd($event, catOrProduct)"
  @contextmenu.prevent="rightClickProd($event, catOrProduct)">

....ViewMagic....
</v-col>
............
</template>

<script>
.........
  props: {
    catIdFromUrl: {
      type: String,
      default: undefined
    }
  },

  computed: {
    // https://stackoverflow.com/questions/40322404/vuejs-how-can-i-use-computed-property-with-v-for
    ...mapState('catOrProducts', ['catOrProducts']),
  },

  watch: {
    '$route.path'() { this.bindCatsWithProducts(this.catIdFromUrl) },
  },

  mounted() {
    this.bindCatsWithProducts(this.catIdFromUrl)
  },

  methods: {
    leftClickProd(event, catOrProd) {
      event.preventDefault()
      if (typeof (catOrProd.parentCat) === 'string') { // when parentCat exists we have a Category entry
        this.$router.push({ name: 'sale', params: { catIdFromUrl: catOrProd.id } })
        // this.bindCatsWithProducts(catOrProd.id)
      } else {
        // ToDo: Replace with buying-routine
        this.$refs.ProductMenu.open(catOrProd, event.clientX, event.clientY)
      }
    },
  }
</script>

编辑 24.09.2020

我已将绑定逻辑更改为

const mutations = {
  setCatProd(state, val) {
    state.catOrProducts = val
  },
}

const actions = {
async bindCatsWithProducts({ commit, dispatch }, CatID) {
    const contain = CatID || 'nC'
    const arr = []

    catsColl.where('parentCat', '==', contain).orderBy('name', 'asc')
      .onSnapshot(snap => {
        snap.forEach(cat => {
          arr.push({ id: cat.id, ...cat.data() })
        })
      })

    productsColl.where('cats', 'array-contains', contain).orderBy('name', 'asc')
      .onSnapshot(snap => {
        snap.forEach(prod => {
          arr.push({ id: prod.id, ...prod.data() })
        })
      })

    commit('setCatProd', arr)
  },
}

这很有效,因为当我在后端更改某些内容时数据会更新。

但是现在每次发生变化时我都会添加一个对象。例如,我改变了价格。现在我明白了:

我不知道为什么,因为我在 Vue 中设置了 key 字段。渲染代码为:

<v-container fluid>
  <v-row
    align="center"
    justify="center"
  >
    <v-col
      v-for="catOrProduct in catOrProducts"
      :key="catOrProduct.id"
      @click.prevent="leftClickProd($event, catOrProduct)"
      @contextmenu.prevent="rightClickProd($event, catOrProduct)"
    >
      <div>
        <TileCat
          v-if="typeof(catOrProduct.parentCat) == 'string'"
          :src="catOrProduct.pictureURL"
          :name="catOrProduct.name"
        />
        <TileProduct
          v-if="catOrProduct.isSold"
          :name="catOrProduct.name"
          ... other props...
        />
      </div>
    </v-col>
  </v-row>
</v-container>

为什么没有正确更新?

【问题讨论】:

  • 那么您的实际问题又是什么?
  • 我是否理解正确,您总是希望拥有最新数量的可用产品?
  • 目前,您的代码非常重要,请查看以下文档:firebase.google.com/docs/firestore/query-data/listen 这允许您评估更改(在 Firestore 中),然后重新设置您的商店数据。
  • @Tallboy:如何获取实时更新。
  • 是的,这可能是最简单的解决方案。您不太可能只从.get() 更改为.onSnapshot - 但我认为您可以处理一些行更改。 :)

标签: javascript firebase vue.js google-cloud-firestore vuex


【解决方案1】:

来自 Vuefire docs,这是您仅使用 Firebase 订阅更改的方式:

// get Firestore database instance
import firebase from 'firebase/app'
import 'firebase/firestore'

const db = firebase.initializeApp({ projectId: 'MY PROJECT ID' }).firestore()

new Vue({
  // setup the reactive todos property
  data: () => ({ todos: [] }),

  created() {
    // unsubscribe can be called to stop listening for changes
    const unsubscribe = db.collection('todos').onSnapshot(ref => {
      ref.docChanges().forEach(change => {
        const { newIndex, oldIndex, doc, type } = change
        if (type === 'added') {
          this.todos.splice(newIndex, 0, doc.data())
          // if we want to handle references we would do it here
        } else if (type === 'modified') {
          // remove the old one first
          this.todos.splice(oldIndex, 1)
          // if we want to handle references we would have to unsubscribe
          // from old references' listeners and subscribe to the new ones
          this.todos.splice(newIndex, 0, doc.data())
        } else if (type === 'removed') {
          this.todos.splice(oldIndex, 1)
          // if we want to handle references we need to unsubscribe
          // from old references
        }
      })
    }, onErrorHandler)
  },
})

我通常会避免任何不必要的依赖,但根据您的目标,您可以使用 Vuefire 添加另一层抽象,或者如您所说,做一些“魔术绑定”

import firebase from 'firebase/app'
import 'firebase/firestore'

const db = firebase.initializeApp({ projectId: 'MY PROJECT ID' }).firestore()

new Vue({
  // setup the reactive todos property
  data: () => ({ todos: [] }),
  firestore: {
    todos: db.collection('todos'),
  },
})

【讨论】:

  • 这只会订阅 one 集合。我的问题是两个集合同时在一个数组中。当我调用 prodColl.get() 和 catchall.get() 时,您可以在问题中清楚地看到这一点
  • 是的,很抱歉,您绝对是对的。在我编辑我的问题之前,我想指出,当您显示数组的每个元素时v-for="catOrProduct in catOrProducts",即使绑定正常工作,Vue won't display 也是一个反应数组。您必须设置watcher 或执行$forceUpdate() 以确保您的数据是最新的。在修复此问题之前,不要依赖屏幕上显示的内容。依靠您的DevTools extension 来查看实时更新。
猜你喜欢
  • 2020-07-11
  • 2021-06-23
  • 2021-09-03
  • 2021-09-26
  • 1970-01-01
  • 2022-07-31
  • 2020-06-03
  • 2019-10-03
  • 1970-01-01
相关资源
最近更新 更多