Problems with Vue.js 3 and Pinia Getters

Viewed 47

I'm switching from Angular to Vue.js and trying to understand the architecture. I'm currently running into a fundamental problem and working with a lot of workarounds that I really only consider temporary solutions.

The main issue here is the collaboration between Vue.js 3 and Pinia. Pinia consists of the Store, Getters and Actions. Sometimes we have very nested objects in the Store and we only need certain parts of it. For that, it's perfect to create a getter, for example, to output only the parts of the object that 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 readonly, this is not possible.

How would one proceed here?

Of course I would like to show you an example to underline my explanations 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 am creating a store here with a very nested object roles. In the getter getElementsForCurrentRole, which I use in the template for a v-for, I only need certain elements.

To make it easier for you to understand, I'm also sending 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>

I loop through the getElementsForCurrentRole getter after using the storeToRefs function mentioned in the documentation to make the data reactive. My problem here is that the data is probably only partially reactive. For example, if I change the value of the Switch element, then the store updates successfully. This works. However, I don't seem to have access to the order of the array I'm looping. As soon as I change the order by drag and drop, I get the message: Write operation failed: computed value is readonly.

I don't understand this and I can't comprehend it.

As a workaround I currently calculate the old and the new index of the record in the array after dragging based on the event and update the store manually. But that can't be the purpose behind it. Have I fundamentally misunderstood something in the architecture? How would one approach such a case?

0 Answers
Related