1

I have a following code for the template:

<script setup>

import {useProductStore} from "@/store/products";
import {ref} from "vue";
import {storeToRefs} from "pinia";

const productStore = useProductStore()

let { products } = storeToRefs(productStore)
let { categories } = storeToRefs(productStore)

const { showProducts } = productStore

</script>

<template>

  <div>
    <div class="row">
      <div class="col-md-2">
        <button class="d-block w-100"
                @click="showProducts('all')">All</button>
        <button class="d-block w-100"
                @click="showProducts(category.id)"
                :key="category.id"
                v-for="category in categories">{{ category.name }}</button>
      </div>
      <div class="col">
        <p v-for="product in showProducts('all')"
            :key="product.id">{{ product.name }}</p>
      </div>
    </div>
  </div>

</template>

And the code for the Pinia store:

import { defineStore } from 'pinia'
import axiosClient from '@/axios'

export const useProductStore = defineStore( 'products', {
    id : 'products',
    state: () => {
        return {
            products: [],
            categories: [],
        }
    },
    actions: {
        async getProducts()
        {

            axiosClient.get('products')
                .then((response) => {
                    const data = response.data

                    this.products = data.products
                    this.categories = data.categories
                })

        }
    },
    getters: {
        showProducts: (state) =>
        {
            return (categoryID) =>
            {
                if(categoryID === 'all')
                {
                    return state.products
                }

                return state.products.filter((product) => product.productCategoryID == categoryID)
            }
        }
    },
})

The initial data is loaded and show,but on click event is not showing filtered data (the showProducts method is fired and products are filtered, but the list is not visually changed).

What am I doing wrong here? What

2
  • Did you tried without destructuring it? const { showProducts } = productStore i guess its losing its reactivity. Try productStore.showProducts Commented Jun 30, 2022 at 11:58
  • This is presumably due to the behaviour of the getter when used with arguments. The Documentation states, that using them the way you do, that the getter is no longer a getter, but a function that is simply invoked, which would suggest, that you have to invoke it after changing them again. Commented Jun 30, 2022 at 12:20

1 Answer 1

4

This is presumably due to the behaviour of the getter when used with arguments. The Documentation states, that using them the way you do, that the getter is no longer a getter, but a function that is simply invoked, which would suggest, that you have to invoke it after changing them again.

I'd suggest to filter the results in the component itself and map the given states.

Something along these lines:

DISCLAIMER I didn't check if the codes runs as it is written below, the code is shown to give a general idea of what I suggest.

Template:

<script setup>
import {useProductStore} from "@/store/products";
import {ref, reactive, computed} from "vue";
import {storeToRefs} from "pinia";

const productStore = useProductStore()

const state = reactive({categoryId: 'all'});

const filteredProducts = computed(() => {
   return state.categoryId === 'all' 
     ? products
     : products.filter((product) => product.productCategoryID === categoryId);
})

let { products } = storeToRefs(productStore)
let { categories } = storeToRefs(productStore)
</script>

<template>

  <div>
    <div class="row">
      <div class="col-md-2">
        <button class="d-block w-100"
                @click="categoryId = 'all'">All</button>
        <button class="d-block w-100"
                @click="categoryId = category.id"
                :key="category.id"
                v-for="category in categories">{{ category.name }}</button>
      </div>
      <div class="col">
        <p v-for="product in filteredProducts"
            :key="product.id">{{ product.name }}</p>
      </div>
    </div>
  </div>
</template>

Pinia:

import { defineStore } from 'pinia'
import axiosClient from '@/axios'

export const useProductStore = defineStore( 'products', {
    id : 'products',
    state: () => {
        return {
            products: [],
            categories: [],
        }
    },
    actions: {
        async getProducts()
        {
          axiosClient.get('products')
            .then((response) => {
              const data = response.data
              this.products = data.products
              this.categories = data.categories
            })
        }
    },
})
Sign up to request clarification or add additional context in comments.

1 Comment

@Sasha You're welcome. Btw another suggestion from my end is to omit the arbitrary categoryID 'all' and to simply use either null or undefined as your indicator whether or not you want to filter the results. This loosens the coupling between the component and the store and provides an initial behaviour of "give me everything" if no limiting factor (like categoryId) is given.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.