Refactored a third time to rely on per route gaurds to trigger vuex actions and pass state into the ShowEvent component

master
androiddrew 6 years ago
parent 2c574c549f
commit 19a7dd3f31

@ -6,18 +6,93 @@
- Axios Interceptors - Axios Interceptors
- Not optimal for multiple API calls - Not optimal for multiple API calls
- We do not want to show the user the template before the data is available. - We do not want to show the user the template before the data is available.
- We would probably want to use a little indirection using Vuex to track how many APIs are waiting responses before calling done. This would mean having some global state that is tracking this count, starting and stopping the progress as needed.
- In-component Route Gaurds - In-component Route Gaurds
- Global and Per-Route Gaurds (Unltimately will use this.) - Global and Per-Route Gaurds (Unltimately will use this.)
- Use the `npx json-server -d 1500 --watch ./db.json` command to simulate a slow network response. - Use the `npx json-server -d 1500 --watch ./db.json` command to simulate a slow network response.
- Good reasons to use interceptors: - Good reasons to use interceptors:
- On request to set authorization tokens - On request to set authorization tokens
- On respinse to format or filter data before it reaches into your app - On respinse to format or filter data before it reaches into your app
- On response to catch 401 not authorized responses - On response to catch 401 not authorized responses. You might want to trigger and action to renew your token, or redirect to login.
- Vue Router provides our solution with 3 addtional hooks called route navigation gaurds. All must call the `next` function.
1. `beforeRouteEnter(routeTo, routeFrom, next)` Which is called before the component is created. You will not have access to `this` in the targeted component.
2. `beforeRouteUpdate(routeTo, routeFrom, next)` Called when the route changes, but still using the same component. Has access to `this`.
3. `beforeRouteLeave(routeTo, routeFrom, next)` Called when the this component is navigated away from. Has access to `this`.
- `routeTo` is the Route being navigated to
- `routeFrom` is the current Route
- `next` is a function that MUST be called to resolve the hook.
- `next()` with no params will confirm the navigation
- `next(false)` will cancel the navigation.
- `next('/')` with a path will redirect to the provided path. We can also specify a named path `next({ name: 'event-list' })` if we wanted. You can put any options that you could put in your router link also with this method.
- An example of using `beforeRouteLeave(routeTo, routeFrom, next)` might be to prompt a user to save thier work before navigating away from the page.
```
beforeRouteLeave(routeTo, routeFrom, next) {
const answer = window.confirm('Do you really want to leave? You have unsaved changes`)
if (answer){
next()
} else {
next(false)
}
}
```
### Global Route Gaurds & Per Route Gaurds
Global Route Gaurds allow us to add hooks that run whenever nagigation is triggered. These gaurds are called on the `router` object. We need to set the `router` object to a contant.
- `beforeEach` runs before _navigating_ to any component and must call `next()`
```
router.beforeEach((routeTo, routeFrom, next) => {
next()
})
```
- `afterEach` runs before the target route component is _created_ and doesn't have a `next` method to call.
```
router.afterEach((routeTo, routeFrom) => {
...
})
```
- The ordering of hooks are as follows:
1. `router.beforeEach()` Global gaurd
2. `beforeEnter()` Per route gaurd
3. `beforeRouteEnter()` Router guard in component
4. `router.afterEach()` Global gaurd
5. `beforeCreate()` Component hook
6. `created()` Component hook
7. ...
### Per Route Gaurds
Per Route gaurds let us tap into for example the `beforeEnter` hook within our `/src/router.js` which will run before routing to the component.
```
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'event-list',
component: EventList
beforeEnter((routeTo, routeFrom, next) => {
fetchEvent()
next()
}
},
```
Since all the API action calls are occuring in the router lifecycle hooks at this time. You can actually remove all vuex code from the component. This could make your code simpler and easier to maintain. You will need to ensure that the actions in your vuex store are returning the objects you are working with however to ensure this works.
## Following along? ## Following along?
We encourage you to follow the course on Vue Mastery, and code along with us. This course has tags representing the start and finish of each level, just in case you get stuck. Here's the start and ending code of each lesson, if you'd like to download them. We encourage you to follow the course on Vue Mastery, and code along with us. This course has tags representing the start and finish of each level, just in case you get stuck. Here's the start and ending code of each lesson, if you'd like to download them.
To achieve this you would need to have the state returned from the api call and pass that into the target component as a prop.
| Lesson | | | | Lesson | | |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| 2 - Vue CLI | n/a | [Finished Code](https://github.com/Code-Pop/real-world-vue/releases/tag/lesson2-cli-finish) | | 2 - Vue CLI | n/a | [Finished Code](https://github.com/Code-Pop/real-world-vue/releases/tag/lesson2-cli-finish) |

@ -3,16 +3,20 @@ import Router from 'vue-router'
import EventCreate from './views/EventCreate.vue' import EventCreate from './views/EventCreate.vue'
import EventList from './views/EventList.vue' import EventList from './views/EventList.vue'
import EventShow from './views/EventShow.vue' import EventShow from './views/EventShow.vue'
import NProgress from 'nprogress'
import store from '@/store/store'
Vue.use(Router) Vue.use(Router)
export default new Router({ // Must use const for Global Route gaurd calls.
const router = new Router({
mode: 'history', mode: 'history',
routes: [ routes: [
{ {
path: '/', path: '/',
name: 'event-list', name: 'event-list',
component: EventList component: EventList
// This path needs to call dispatch using beforeEnter like the event-show
}, },
{ {
path: '/event/create', path: '/event/create',
@ -23,7 +27,26 @@ export default new Router({
path: '/event/:id', path: '/event/:id',
name: 'event-show', name: 'event-show',
component: EventShow, component: EventShow,
props: true props: true,
beforeEnter(routeTo, routeFrom, next) {
store.dispatch('event/fetchEvent', routeTo.params.id).then(event => {
routeTo.params.event = event
next()
})
}
} }
] ]
}) })
router.beforeEach((routeTo, routeFrom, next) => {
console.log(`Leaving: ${routeFrom.path} moving to ${routeTo.path}`)
NProgress.start()
next()
})
router.afterEach((routeTo, routeFrom) => {
routeFrom
NProgress.done()
})
export default router

@ -1,5 +1,5 @@
import axios from 'axios' import axios from 'axios'
import NProgress from 'nprogress' // import NProgress from 'nprogress'
const apiClient = axios.create({ const apiClient = axios.create({
baseURL: `http://localhost:3000`, baseURL: `http://localhost:3000`,
@ -12,15 +12,15 @@ const apiClient = axios.create({
// Interceptor definition. This is like middleware // Interceptor definition. This is like middleware
apiClient.interceptors.request.use(config => { // apiClient.interceptors.request.use(config => {
NProgress.start() // NProgress.start()
return config // return config
}) // })
apiClient.interceptors.response.use(response => { // apiClient.interceptors.response.use(response => {
NProgress.done() // NProgress.done()
return response // return response
}) // })
export default { export default {
getEvents(perPage, page) { getEvents(perPage, page) {

@ -62,10 +62,12 @@ export const actions = {
if (event) { if (event) {
commit('SET_EVENT', event) commit('SET_EVENT', event)
return event // added to return the event to our beforeEnter Per route gaurd
} else { } else {
EventService.getEvent(id) return EventService.getEvent(id) // we needed to add return so that the promise is returned to the ShowEvents beforeRouteEnter hook
.then(response => { .then(response => {
commit('SET_EVENT', response.data) commit('SET_EVENT', response.data)
return response.data
}) })
.catch(error => { .catch(error => {
const notification = { const notification = {

@ -7,14 +7,17 @@
<h5>Category: {{ event.category }}</h5> <h5>Category: {{ event.category }}</h5>
</div> </div>
<BaseIcon name="map"><h2>Location</h2></BaseIcon> <BaseIcon name="map">
<h2>Location</h2>
</BaseIcon>
<address>{{ event.location }}</address> <address>{{ event.location }}</address>
<h2>Event details</h2> <h2>Event details</h2>
<p>{{ event.description }}</p> <p>{{ event.description }}</p>
<h2>Attendees <h2>
Attendees
<span class="badge -fill-gradient">{{ event.attendees ? event.attendees.length : 0 }}</span> <span class="badge -fill-gradient">{{ event.attendees ? event.attendees.length : 0 }}</span>
</h2> </h2>
<ul class="list-group"> <ul class="list-group">
@ -25,17 +28,35 @@
</div> </div>
</template> </template>
<script> <script>
import { mapState, mapActions } from 'vuex' //import { mapState, mapActions } from 'vuex' // don't need mapActions since we aren't calling it after the beforeRouteEnter hook
// import { mapState } from 'vuex'
// import NProgress from 'nprogress'
// import store from '@/store/store.js'
export default { export default {
props: ['id'], // props: ['id'] //,
created() { props: {
this.fetchEvent(this.id) event: {
}, type: Object,
computed: mapState({ required: true
event: state => state.event.event }
}), }
methods: mapActions('event', ['fetchEvent']) // beforeRouteEnter(routeTo, routeFrom, next) {
// NProgress.start()
// // We don't have access to this here so we need to pull in the vuex store to call this action directly
// store.dispatch('event/fetchEvent', routeTo.params.id).then(() => {
// NProgress.done()
// next()
// })
// },
// created() hook is unneccessary now because we are using the beforeRouteEnter to call the API and update the vuex store.
// created() {
// this.fetchEvent(this.id)
// },
// computed: mapState({
// event: state => state.event.event
// })
//, This is also redundant because we are not calling this again in this component
// methods: mapActions('event', ['fetchEvent'])
} }
</script> </script>
<style scoped> <style scoped>

Loading…
Cancel
Save