Search code examples
vue.jsvue-componentvuejs3vuexgetter

VUE3 use different v-for for the same component


I have a JobComponent.vue component where I fetch data from a VUEX Store. This component is used on two separate pages, first page Home.vue and second page AllJobs.vue. In AllJobs.vue I used JobComponent.vue and everything is works fine, it's rendering all the jobs, but, here comes the problem...

In Home.vue I want to render only the last 5 jobs, so in store I make a getter that slice me only the latest 5 jobs.

How can I use this latestJobs from getters on the same component?

When I import the component in Home.vue page I can't use another v-for direct on the component...

here you can see my project structure and files

Home.vue

<template>
  <div class="cards-container">
      <JobComponent />
  </div>
</template>

JobComponent.vue

<template>
  <div v-for="job in allJobs" :key="job.id" class="card">
    <div class="position">{{ job.position }}</div>
    <div class="department">{{ job.department }}</div>
    <div class="location">
      <span class="material-symbols-outlined">location_on</span>
      {{ job.location }}
    </div>
    <span class="material-symbols-outlined right-arrow">arrow_right_alt</span>
    <span @click="deleteJob(job.id)" class="material-symbols-outlined right-arrow">delete</span>
  </div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex';

export default {
  methods: {
    ...mapActions(['fetchJobs', 'deleteJob']),
  },
  computed: mapGetters(['allJobs']),
  created() {
    this.fetchJobs();
  }
}
</script>

store.js (vuex)

const getters = {
    allJobs: (state) => state.jobs,
    
    latestJobs: (state) => {
        const response = state.jobs.slice(0, 5);

        return response;
    }
};

Solution

  • Your component should be as independent as possible from the store. It's role is to display what ever is provided so it could be reused as you want, using props :

    JobComponent.vue

    <template>
      <div class="card">
        <div class="position">{{ position }}</div>
        <div class="department">{{ department }}</div>
        <div class="location">
          <span class="material-symbols-outlined">location_on</span>
            {{ location }}
        </div>
        <span class="material-symbols-outlined right-arrow">arrow_right_alt</span>
        <span @click="$emit('deleteJob', id)" class="material-symbols-outlined right-arrow">delete</span>
      </div>
    </template>
    
    <script>
    export default {
      props: {
        id: string,
        position: string,
        department: string,
        location: string
      }
    }
    </script>
    

    In this component you only display the provided data, and leave the responsibility of the parent component to choose how many components to display.

    Home.vue

    <template>
      <div class="cards-container">
        <JobComponent v-for="job in jobs" :key="job.id" :id="job.id" :position="job.position" :department="job.department" :location="job.location" @delete-job="deleteJob" />
      </div>
    </template>
    
    <script>
    export default {
      created() {
        this.$store.dispatch('fetchJobs')
      },
      computed: {
        jobs() {
          return this.$store.getters['latestJobs'] // Or allJobs, just make sure your getter returns an array even if no jobs are loaded yet.
        }
      },
      methods: {
        deleteJob() {
          // Your logic for job delete
        }
      }
    }
    </script>