Search code examples
vue.jsvue-componentpinia

Vue ChildComponent (GrandchildComponent) should update a list of the ParentComponent


I have a DocumentView.vue component. This component has a DocumentListingView.Vue component. The DocumentListingView.vue has a DocumentItem.vue componente. In the DocumentItem.vue componente the respective document can be deleted (by api call). After deletion I want the DocumentListingView.vue to be updated. That means that the child has to tell the parent, please update your DocumentListingView by firing a new api call. I know that with $emit and pinia store you can implement the child - parent communication. But I just don't know how it should look architecturally.

Problem: I am trying to implement it with pinia. Unfortunately I don't know the last step, how to update the data in the listing.

Question: What is the theoretical concept? The child element should update the list of the ParentView?

Below you can see the files I use for this. They are reduced to the essential: DocumentView.vue (Parent), DocumentListingView.vue (child), DocumentItem.vue (Grandchild), apiData.js (pinia).

DocumentView.vue

<script setup>
  import { ref, computed as compi, onMounted } from 'vue'
  import DocumentListing from './document/DocumentListingView.vue'
  import { useApiDataStore } from '@/stores/apiData';
  
  let list = {};
  const apiData = useApiDataStore(); // init stores for api call
  
  async function getDocumentList() {  
    await apiData.refreshDocumentList();
    list = await apiData.listData;
  }    
  
  getDocumentList();
</script>

<template>
  <div>
    <DocumentListing :documents="list"/>    
  </div>
</template>

DocumentListingView.vue

<template>
    <h2>Documents</h2>
    <div class="documentListing">        
        <div v-for="item in documents">
            <DocumentItem :data="item"></DocumentItem>        
        </div>        
    </div>
</template>

<script lang="ts" setup>
  import DocumentItem from '../../components/DocumentItem.vue'

  defineProps({
    documents: Object,
  })

</script>

DocumentItem.vue

<script setup>
    import { useApiDataStore } from '@/stores/apiData';

    defineProps({
        data: Object,
    });
    
    const apiData = useApiDataStore();
    const removeDocument = async (document) => {
      // api call to remove document
      apiData.refreshDocumentList();
    }
    
</script>

<template>
  <div>
  <button @click="removeDocument(data)">Delete Doument></button>
  </div>
</template>

apiData.js

import { defineStore } from 'pinia'

export const useApiDataStore = defineStore('apiData', {
    id: 'apiData',
    state: () => ({
        listData: {}
    }),
    actions: {
        async refreshDocumentList() {
            const endpoint = "http://localhost:8888/api.php?list";
            const response = await fetch(endpoint);
            const json = await response.json();          
            this.listData = json;
        }
    }
  })

Solution

  • You can solve this, when you will use in you DocumentView.vue the state property apiData.listData instead of local list property. I commented the changes.

    <script setup>
      import { ref, computed as compi, onMounted } from 'vue'
      import DocumentListing from './document/DocumentListingView.vue'
      import { useApiDataStore } from '@/stores/apiData';
      
      // let list = {}; //remove this line
      const apiData = useApiDataStore(); // init stores for api call
      
      async function getDocumentList() {  
        await apiData.refreshDocumentList();
        // list = await apiData.listData; // remove this line
      }    
      
      getDocumentList();
    </script>
    
    <template>
      <div>
        <DocumentListing :documents="apiData.listData"/>  <!--   replace list with  apiData.listData -->
      </div>
    </template>