Programing

Vue, firestore: 컬렉션을 병합한 후 라이브 데이터를 표시하는 방법

c10106 2022. 4. 11. 20:39
반응형

Vue, firestore: 컬렉션을 병합한 후 라이브 데이터를 표시하는 방법

아래 편집 참조


지난번 질문보다 많이 나아졌지만, 며칠 일을 하고 나니 또 막힌다.

Gogles Could Firestore의 데이터에 Vue, Vue-router, Vuex 및 Vuetify 사용

내 데이터를 라이브로 업데이트하고 싶지만, 이 방법을 찾을 수가 없어.상품과 카테고리를 하나의 컬렉션으로 옮기는 것과 같은 구조조정이 필요한가?아니면 이 일을 성사시키기 위한 어떤 바인딩이나 질의 마법이 있는가?아래에서도 볼 수 있듯이, 클릭으로 데이터를 꽤 잘 로딩하지만, 나는 라이브 바인딩이 필요하다. 왜냐하면 당신은 페이지를 열 수 있고 누군가 마지막 조각을 팔 수 있기 때문이다. (Left = 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 파일의 일부분이다.불필요한 부분은 생략했다.모든 것을 열려면 a에 소품이 있는 경로가 있고 카테고리를 클릭하면 라우터가 다음 카테고리로 전송된다.(이 방법으로 브라우저 기능을 사용하여 다시 이동할 수 있음).판매.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>

EDIT 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)
  },
}

이것은 백엔드에서 무언가를 변경하면 데이터가 업데이트되기 때문에 효과가 있다.

하지만 지금은 무언가가 바뀔 때마다 사물이 추가된다.예를 들어 나는 가격을 변경했다.이제 알겠다:

왜 그런지 모르겠네, 왜냐면 난 그 여자애가 있거든.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>

왜 이것이 올바르게 업데이트되지 않는가?

Vuefire 문서에서 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'),
  },
})

참조URL: https://stackoverflow.com/questions/63945128/vue-firestore-how-to-display-live-data-after-merging-collections

반응형