Search code examples
javascriptvue.jsvuejs2vuexvuex-modules

How to correctly pass paramters to vuex mapActions


I have a ProjectPage.vue page that displays issues in a v-data-table. Projects are retrieved from a server api call in the sidebar and displayed there. After I choose a project, I would like to use that project's id to get its issues from the server. Is it possible to do so using Vuex.
Is it possible using Vuex, that each project can use the same .js file to retreive its issues, since there could be any number of projects.

What I am trying to do is from VerticalNavMenu.vue, pass an id and project as props to ProjectPage, so I can pass the id as a parameter to mapAction inside ProjectPage to retreive its issues.

The I'm way doing it now is not working. The table has no data available when I open a project's table

I hope Peoject_Pages.js helps understand what I'm asking about.

VerticalNavMenu.vue (related template lines are 38 -> 48)

<template>
  <v-navigation-drawer
    :value="isDrawerOpen"
    app
    expand-on-hover
    mini-variant-width="60px"
    floating
    width="260"
    class="app-navigation-menu"
    :right="$vuetify.rtl"
    @input="val => $emit('update:is-drawer-open', val)"
  >
    <!-- Navigation Header -->
    <div class="vertical-nav-header d-flex items-center ps-6 pe-5 pt-5 pb-2">
      <router-link to="/" class="d-flex align-center text-decoration-none">
        <v-slide-x-transition>
          <h2 class="app-title text--primary">ITrackerHub</h2>
        </v-slide-x-transition>
      </router-link>
    </div>

    <!-- Navigation Items -->
    <v-list expand shaped class="vertical-nav-menu-items pr-5">
      <nav-menu-link
        title="Dashboard"
        :to="{ name: 'dashboard' }"
        :icon="icons.mdiHomeOutline"
      ></nav-menu-link>

      <v-list>
        <v-list-group :prepend-icon="icons.mdiTelevisionGuide">
          <template v-slot:activator>
            <v-list-item-content>
              <v-list-item-title v-text="'My Projects'"></v-list-item-title>
            </v-list-item-content>
          </template>

          <v-list-item v-for="(project, index) in ProjectList" :key="index">
            <v-icon class="mx-2">{{ icons.mdiAccountGroup }}</v-icon>
            <v-list-item-content>
              <router-link
                class="d-flex align-center text-decoration-none black--text"
                :to="{ name: 'ProjectPage', params: { id: project.id, project} }"
              >
                {{ project.title }}
              </router-link>
            </v-list-item-content>
          </v-list-item>
        </v-list-group>
      </v-list>

      <nav-menu-link title="My Issues" :to="{ name: 'MyIssues' }" :icon="icons.mdiBookEditOutline"></nav-menu-link>
      <nav-menu-link
        style="position:relative; top:70px;"
        title="Account Settings"
        :to="{ name: 'pages-account-settings' }"
        :icon="icons.mdiAccountCogOutline"
      ></nav-menu-link>

      <nav-menu-link
        style="position: relative; top: 200px"
        title="Create Project"
        :to="{ name: 'CreateProject' }"
        :icon="icons.mdiPlusMinus"
      ></nav-menu-link>
    </v-list>
  </v-navigation-drawer>
</template>

<script>
// eslint-disable-next-line object-curly-newline
import {
  mdiHomeOutline,
  mdiAlphaTBoxOutline,
  mdiEyeOutline,
  mdiCreditCardOutline,
  mdiTable,
  mdiFileOutline,
  mdiFormSelect,
  mdiAccountCogOutline,
  mdiAccountGroup,
  mdiAccountMultiple,
  mdiTelevisionGuide,
  mdiBookEditOutline,
  mdiPlusMinus,
} from '@mdi/js'

// import NavMenuSectionTitle from './components/NavMenuSectionTitle.vue'
import NavMenuGroup from './components/NavMenuGroup.vue'
import NavMenuLink from './components/NavMenuLink.vue'
import { mapGetters, mapActions } from 'vuex'


export default {
  components: {
    // NavMenuSectionTitle,
    NavMenuGroup,
    NavMenuLink,
  },
 
  computed: {
    ...mapGetters(['ProjectList'])
  },
  

  methods: {
    ...mapActions(['fetchProjects'])
  },

  created() {
    // this.getProjectList()
    this.fetchProjects()
  },

  props: {
    isDrawerOpen: {
      type: Boolean,
      default: null,
    },
  },

  setup() {
    return {
      icons: {
        mdiHomeOutline,
        mdiAlphaTBoxOutline,
        mdiEyeOutline,
        mdiCreditCardOutline,
        mdiTable,
        mdiFileOutline,
        mdiFormSelect,
        mdiAccountCogOutline,
        mdiAccountGroup,
        mdiAccountMultiple,
        mdiTelevisionGuide,
        mdiBookEditOutline,
        mdiPlusMinus,
      },
    }
  },
}
</script>

<style lang="scss" scoped>
.app-title {
  font-size: 1.25rem;
  font-weight: 700;
  font-stretch: normal;
  font-style: normal;
  line-height: normal;
  letter-spacing: 0.3px;
}

// ? Adjust this `translateX` value to keep logo in center when vertical nav menu is collapsed (Value depends on your logo)
.app-logo {
  transition: all 0.18s ease-in-out;
  .v-navigation-drawer--mini-variant & {
    transform: translateX(-10px);
  }
}

@include theme(app-navigation-menu) using ($material) {
  background-color: map-deep-get($material, 'background');
}

.app-navigation-menu {
  .v-list-item {
    &.vertical-nav-menu-link {
      ::v-deep .v-list-item__icon {
        .v-icon {
          transition: none !important;
        }
      }
    }
  }
}
</style>

NavBar.js

import axios from 'axios'

const state = {
    Projects: [],
}

const getters = {
    ProjectList: (state) => state.Projects
}

const actions = {
    async fetchProjects({ commit }) {
        const response = await axios.get('https://fadiserver.herokuapp.com/api/v1/my-projects/')

        commit('setProjects', response.data)
    }
}

const mutations = {
    setProjects: (state, Projects) => (state.Projects = Projects)
}

export default {
    state,
    getters,
    actions,
    mutations
}

ProjectPage.vue

<template>
  <v-card>
    <v-card-title class="text-center justify-center py-6">
      <h1 class="font-weight-bold text-h2 basil--text">
        {{ project.title }}
      </h1>
    </v-card-title>
    <v-tabs v-model="tab" background-color="primary" dark centered>
      <v-tab v-for="item in items" :key="item.tab">{{ item.tab }}</v-tab>
    </v-tabs>

    <v-tabs-items v-model="tab">
      <v-tab-item v-for="item in items" :key="item.tab">
        <v-card flat>
          <v-card v-if="item.tab == 'Issues'">
            <template>
              <div class="text-center">
                <v-dialog v-model="dialog" width="500">
                  <template v-slot:activator="{ on }">
                    <v-btn class="success" dark v-on="on">
                      <v-icon align-self: left>
                        mdi-plus-thick
                      </v-icon>
                      Add Issue
                    </v-btn>
                  </template>
                  <v-card>
                    <v-card-title>
                      <h2>Add Issue</h2>
                    </v-card-title>
                    <v-card-text>
                      <v-form class="px-3">
                        <v-text-field v-model="title" label="Title"></v-text-field>
                        <v-textarea v-model="description" label="Description"></v-textarea>
                        <v-select
                          item-text="text"
                          item-value="value"
                          :items="time_est"
                          v-model="time_estimate"
                          label="Time Estimate"
                        ></v-select>

                        <v-select
                          item-text="title"
                          item-value="id"
                          :items="issueType"
                          v-model="issue_type"
                          label="Issue Type"
                        ></v-select>
                        <v-select
                          item-text="title"
                          item-value="id"
                          v-model="issue_status"
                          label="Issue Status"
                          :items="issueStatus"
                        ></v-select>
                        <v-select
                          item-text="title"
                          item-value="id"
                          :items="issueSeverity"
                          v-model="issue_severity"
                          label="Issue Severity"
                        ></v-select>
                        <v-spacer></v-spacer>
                        <v-btn
                          flat
                          @click="
                            postIssue()
                            reloadPage()
                          "
                          class="success mx-0 mt-3"
                        >
                          <v-icon align-self:left>mdi-content-save-check-outline</v-icon> Save</v-btn
                        >
                      </v-form>
                    </v-card-text>
                  </v-card>
                </v-dialog>
              </div>
            </template>

            <v-card>
              <v-data-table
                :headers="headers"
                :items="Project_Issues"
                item-key="full_name"
                class="table-rounded"
                hide-default-footer
                enable-sort
                @click:row="handleClick"
              >
              </v-data-table>
            </v-card>
          </v-card>
        </v-card>
      </v-tab-item>
    </v-tabs-items>
  </v-card>
</template>

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

export default {
  props: ['id', 'project'],

  computed: {
    ...mapGetters(['Project_Issues']),
  },

  data() {
    return {
      tab: null,
      items: [{ tab: 'Issues' }, { tab: 'Calender' }, { tab: 'About' }],

      title: '',
      description: '',
      time_estimate: '',
      issue_type: '',
      issue_status: '',
      issue_severity: '',
      time_est: [
        { value: '1', text: '1' },
        { value: '2', text: '2' },
        { value: '3', text: '3' },
        { value: '4', text: '4' },
        { value: '5', text: '5' },
        { value: '6', text: '6' },
        { value: '7', text: '7' },
        { value: '8', text: '8' },
      ],
    }
  },

  setup() {
    return {
      headers: [
        { text: 'Title', value: 'title' },
        { text: 'Description', value: 'description' },
        { text: 'Estimate', value: 'time_estimate' },
        { text: 'Assignees', value: 'user' },
        { text: 'Type', value: 'issueType' },
        { text: 'Status', value: 'issueStatus' },
        { text: 'Severity', value: 'issueSeverity' },
      ],
    }
  },

  methods: {
    ...mapActions(['fetchProjectIssueList']),
  
    handleClick(issue) {
      this.$router.push({
        name: 'IssuePage',
        params: { id: issue.id, issue },
      })
    },
    postIssue() {
      axios
        .post('https://fadiserver.herokuapp.com/api/v1/my-issues/', {
          title: this.title,
          description: this.description,
          time_estimate: this.time_estimate,
          userid: 'f3260d22-8b5b-4c40-be1e-d93ba732c576',
          projectid: this.id,
          issueTypeId: this.issue_type,
          issueStatusId: this.issue_status,
          issueSeverityId: this.issue_severity,
        })
        .then(response => {
          console.log(response)
        })
        .catch(error => {
          console.log(error)
        })
    },
    reloadPage() {
      window.location.reload()
    },
  },
  mounted() {
    this.fetchProjectIssueList(this.id)
  },
}
</script>

<style scoped>
.v-btn {
  left: 43%;
}
</style>

Project_Page.js

import axios from 'axios'

const state = {
    issuesList: [],
}

const getters = {
    Project_Issues: (state) => state.issuesList
}

const actions = {
    async fetchProjectIssueList({ commit }, {projectid}) {
        const response = await axios.get('https://fadiserver.herokuapp.com/api/v1/my-issues-titles/?projectid=' + projectid)
    
        commit('setProjectIssues', response.data)
    },
}

const mutations = {
    setProjectIssues: (state, issuesList) => (state.issuesList = issuesList)
}

export default {
    state,
    getters,
    actions,
    mutations
}

Solution

  • There are 2 ways of solving your problem.

    • Or you pass an object with the key projectid in the fetchProjectIssueList method
    • Or you do not need to destructure the object in the fetchProjectIssueList method
    this.fetchProjectIssueList({ projectid: this.id })
    ...
    async fetchProjectIssueList({ commit }, {projectid}) { ... }
    

    or

    this.fetchProjectIssueList(this.id)
    ...
    async fetchProjectIssueList({ commit }, projectid) { ... }