Implementing the services and Vuex store

Before we implement the Vuex store, let's get those services created. We will need to create teamService in the front-end/src/services/teams.js file, which looks like this:

...
export default {
/**
* Create a team
* @param {*} detail the detail of the team
*/
create (detail) {
return new Promise((resolve, reject) => {
axios.post('/teams', detail).then(({data}) => {
resolve(data)
}).catch((error) => {
reject(errorParser.parse(error))
})
})
}
}

As you can see, this service only has one method, create(), which send the team's detail to /api/teams in a POST request.

We will also need to create boardService, which is very similar to teamService, as you can see:

...
export default {
/**
* Create a new board
* @param {*} detail the board detail
*/
create (detail) {
return new Promise((resolve, reject) => {
axios.post('/boards', detail).then(({data}) => {
resolve(data)
}).catch((error) => {
reject(errorParser.parse(error))
})
})
}
}

The only difference is that the request is sent to /api/boards. As for the parameter details, it is up to the Vue instance, in our case HomePage.vue, to decide which data will be included, as services do not need to care about it.

The last service we need to create at this stage is meService, and here is how it looks:

...
export default {
/**
* Fetch current user's name, boards, and teams
*/
getMyData () {
return new Promise((resolve, reject) => {
axios.get('/me').then(({data}) => {
resolve(data)
}).catch((error) => {
reject(errorParser.parse(error))
})
})
}
}

As you can see, all it does is send a GET request to /api/me.

Now, let's implement the Vuex store. The default generated front-end/src/store.js puts Vuex's actions, mutations, getters, and state in a single file. It would be more practical to reorganize it by separating actions, mutations, and getters into separate files or by separating it by modules. For large applications, it is better to split Vuex by modules. Inside each module, we can put actions, mutations, and getters in separate files. For our application, we will go with the first way. Let's delete the front-end/src/store.js file and create the front-end/src/store folder with the following structure:

.
└── store
├── actions.js
├── getters.js
├── index.js
└── mutations.js

Here is how store/index.js looks:

import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters'
import * as actions from './actions'
import mutations from './mutations'
import createLogger from 'vuex/dist/logger'

Vue.use(Vuex)

const state = {
user: {
name: null
},
teams: [/* {id, name} */],
boards: [/* {id, name, description, teamId} */]
}

export default new Vuex.Store({
state,
getters,
actions,
mutations,
plugins: process.env.NODE_ENV !== 'production'
? [createLogger()]
: []
})

As you can see, we define the data structure of state and then create an instance of Vuex.Store with state, getters, actions, and mutations.

The comments we put inside teams and boards are reminders of what fields the items in these two arrays will have.

By design, only mutations can change the state saved in the Vuex store. Here is how our store/mutations.js looks:

export default {
updateMyData (state, data) {
state.user.name = data.user.name
state.teams = data.teams
state.boards = data.boards
},
addTeam (state, team) {
state.teams.push(team)
},
addBoard (state, board) {
state.boards.push(board)
}
}

As you can see, we define three mutation methods, updateMyData(), addTeam(), and addBoard(). The updateMyData() method is for updating the user's name, and all of the teams and boards the user can access with the result of the /api/me request. The addTeam() method and addBoard() method are for adding the newly created team and the newly created boards respectively. The first parameter, state, in these methods is provided by Vuex. The second parameter is the payload of the mutation, which is passed through from the actions.

As mentioned earlier, only actions can commit mutations. Let's see how our store/actions.js looks:

import meService from '@/services/me'

export const getMyData = ({ commit }) => {
meService.getMyData().then(data => {
commit('updateMyData', data)
})
}

export const addTeam = ({commit}, team) => {
commit('addTeam', team)
}

export const addBoard = ({commit}, board) => {
commit('addBoard', board)
}

As you can see, we define three actions, getMyData, addTeam, and addBoard. We call meService.getMyData(), which is an asynchronous call, inside the getMyData action. Then, we commit the updateMyData mutation with the result of the call. In this way, we save the server's response into the store. Inside addTeam and addBoard actions, we simply commit a mutation with the data passed to the action. The first argument passed to an action method is the context provided by Vuex. It has a commit method. We use argument destructuring here to simplify the method's parameter.

As you might remember, actions are dispatched from Vue components. For now, let's see how to do that from PageHeader.vue to get the current user's data, as shown in the following code snippet:

<script>
export default {
name: 'PageHeader',
created () {
this.$store.dispatch('getMyData')
}
}
</script>

We use this.$store.dispatch('getMyData') inside the created() life cycle hook of the Vue instance, and we will see how to dispatch addTeam and addBoard actions later.

Now, let's take a look at store/getters.js, which looks as follows:

export const user = state => state.user

export const hasBoards = state => {
return state.boards.length > 0
}

export const personalBoards = state => {
return state.boards.filter(board => board.teamId === 0)
}

export const teamBoards = state => {
const teams = []

state.teams.forEach(team => {
teams.push({
id: team.id,
name: team.name,
boards: state.boards.filter(board => board.teamId === team.id)
})
})

return teams
}

As you can see, we define four gettersuser, hasBoards, personalBoards, and teamBoards. The first three getters are straightforward. Inside the last one, teamBoards, instead of sending the teams directly from the state, we create a new array and filter state.boards to get those that belong to the related team.

Now, let's see how we can use getters. To access getters, we can use a helper method, mapGetters, provided by Vuex to add these getters into a Vue component as computed properties. The following is the change we make to PageHeader.vue:

<script>
...
import { mapGetters } from 'vuex'

export default {
name: 'PageHeader',
computed: {
...mapGetters([
'user',
'hasBoards',
'personalBoards',
'teamBoards'
])
},
created () {
this.$store.dispatch('getMyData')
},
...
}
</script>

As you can see, we use ...mapGetters([]) to map the required getters into the computed properties. In this way, in the view, we can access these getters directly. For example, use {{ user.name }} to get the user's name.

We won't go into the rest of the details of how to render the boards menu, the teams, and the boards on the home page. You can check the commit history for details. Since the backend /api/me API isn't ready yet, we cannot test the implementation of the Vuex store, unless we use mock data. So, in this commit, you will see store/getters.js is actually returning mock data:

Figure 11.9: Implementing Vuex store commit
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.134.130