search

Home  >  Q&A  >  body text

Issues with Vue.js 3 and Pinia Getters

I'm switching from Angular to Vue.js and trying to understand its architecture. I'm currently stuck with a basic problem and have used a lot of workarounds, but I'm really only looking at temporary solutions.

The main issue here is the collaboration between Vue.js 3 and Pinia. Pinia consists of Store, Getter and Action. Sometimes we have very nested objects in the Store and we only need certain parts of it. It would be perfect to create a getter for this, for example, to output only the part of the object I need.

But what if I want to change exactly those parts of the object from the template? My wish is that the data in the store changes as well. But since getters are read-only, this is not possible.

How to proceed here?

Of course, I want to show you an example to emphasize my explanation with some practice.

export const useGeneralStore = defineStore('general', {
  state: () => {
    return {
      roles: [],
      currentRole: 'basic',
    }
  },
  getters: {
    getElementsForCurrentRole: (state) => {
      let role = state.roles.find((role) => {
        return role.value == state.currentRole;
      });

      if (role) {
        return role.data;
      }
    }
  },
  actions: {}
})

I'm using a very nested object here roles to create a store. In the getter getElementsForCurrentRole I'm using in my v-for template, I only need certain elements.

In order to facilitate everyone's understanding, I will also post the template code here:

<template>
  <div class="element-container">
    <div v-for="cat of elementCategories" :key="cat">
      <h4>{{cat}}</h4>
      <draggable 
        v-model="getElementsForCurrentRole" 
        :group="cat"
        @end="save" 
        item-key="name">
        <template #item="{element}">
          <n-card v-if="element.category == cat" class="element" :class="element.name" :title="element.name" size="small" header-style="{titleFontSizeSmall: 8px}" hoverable>
            <n-switch v-model:value="element.active" @update:value="save(element)" size="small" />
          </n-card>
        </template>
      </draggable>
    </div>
  </div>
</template>

<script setup>
  import { NCard, NSwitch, useMessage } from 'naive-ui';
  import draggable from 'vuedraggable'
  import { usePermissionsStore } from '@/stores/permissions';
  import { storeToRefs } from 'pinia';

  const permissionsStore = usePermissionsStore();
  const { getElementsForCurrentRole } = storeToRefs(permissionsStore);  

  const elementCategories = ['basic', 'professional'];
</script>

After using the storeToRefs function mentioned in the documentation, I looped through the getElementsForCurrentRole getter to make the data reactive. My problem is that the data may be only partially reactive. For example, if I change the value of the Switch element, the store updates successfully. This works. However, I can't seem to access the order of the array I'm looping through. As soon as I change the order via drag and drop, I get the message: Write operation failed: Computed value is read-only.

I don't understand this, I can't understand it.

As a workaround, I currently calculate the old and new index of the record in the array after dragging based on the event, and update the storage manually. But that couldn't be the purpose behind it. Am I fundamentally misunderstanding something in the architecture? How would one deal with such a situation?

P粉668019339P粉668019339393 days ago702

reply all(1)I'll reply

  • P粉034571623

    P粉0345716232023-11-04 15:30:00

    You are using a getter. Getter is a read-only property. To update the value in any pinia store, you must create an action. And you are using v-model which binds and writes when the value changes. Therefore, it can be read using getters, but not written. There are several possibilities for your situation. One of them is creating computed properties and operations. like this

    actions: {
      updateRole(newRole) {
        this.currentRole = newRole
    }

    And computed properties in components:

    const myComputed = computed({
      get: () => getElementsForCurrentRole.value,
      set: (val) => updateRole(val),
    })

    reply
    0
  • Cancelreply