Router for board URL and card URL

We will give each card a unique URL so that users can use that URL to open the card directly, and a card URL will not contain any board information so that when a card is moved between boards, the card URL will always be able to open that card correctly. We will implement the moving of cards between boards feature in a future version.

The card URL will use this format: https://taskagile.com/card/{cardId}/{card-title}. For example, the URL of the Increase my handcraft skill card, whose ID is 4, will be this: https://taskagile.com/card/4/increase-my-handcraft-skill.

So, how are we going to use the card URL to open a card and also have the board fully loaded behind the mask? The answer is by using vue-router. We can add the card route to router.js, as shown in the following:

...
export default new Router({
...
routes: [...
{
path: '/board/:boardId',
name: 'board',
component: BoardPage
}, {
path: '/card/:cardId/:cardTitle',
name: 'card',
component: BoardPage
}]
})

As you can see, component of the card route is also BoardPage. In this way, requests that go to the card URL will be handled by the BoardPage component. This is the first step to the route board URL and card URL. We will need to refactor BoardPage to make both routes work.

Previously, we relied on the following navigation guards of vue-router to trigger the loading of the board as well as unsubscribing from the real-time update channel:

beforeRouteEnter (to, from, next) {
next(vm => {
vm.loadBoard()
})
},
beforeRouteUpdate (to, from, next) {
next()
this.unsubscribeFromRealTimeUpdate()
this.loadBoard()
},
beforeRouteLeave (to, from, next) {
next()
this.unsubscribeFromRealTimeUpdate()
}

Right now, because we map two paths, one for the board URL and the other for the card URL, to the BoardPage component, the vue-router library would not invoke the beforeRouteUpdate guard when we open the page with a board URL and then open a card, close it, and then switch to another card, or when we open the page using a card URL and then switch to another board using the Boards menu at the top. According to the documentation of vue-router (https://router.vuejs.org/guide/advanced/navigation-guards.html#per-route-guard), the beforeRouteUpdate guard will be invoked when we switch from /board/1 to /board/2, but not when we switch from /board/1 to /card/1/card-title1.

Therefore, we cannot rely on the navigation guards to detect the changes to the route when we stay on the BoardPage page. We will need to watch the this.$route object to detect the route change, as in the following:

<script>
...
export default {
name: 'BoardPage',
...
watch: {
'$route' (to, from) {
// Switch from one board to another
if (to.name === from.name && to.name === 'board') {
this.unsubscribeFromRealTimeUpdate(from.params.boardId)
this.loadBoard(to.params.boardId)
}
// Open a card
if (to.name === 'card' && from.name === 'board') {
this.loadCard(to.params.cardId).then(() => {
this.openCardWindow()
})
}
// Close a card
if (to.name === 'board' && from.name === 'card') {
this.closeCardWindow()
this.openedCard = {}
}
}
},
...
}
</script>

As you can see, we use to.name and from.name to detect three scenarios: switching between boards, opening a card, and closing a card. With this watcher, we do not need the navigation guards, beforeRouteEnter and beforeRouteUpdate. We will still need the beforeRouteLeave guard to detect the exit from the board page, which is not detected by the $route watcher.

Inside the mounted() hook of the BoardPage component instance, we will need to trigger the data loading of the board page when users open a board URL or a card URL directly or refresh the page. The refactored mounted() method looks like the following:

mounted () {
console.log('[BoardPage] Mouted')
this.loadInitial()
this.$el.addEventListener('click', this.dismissActiveForms)
// Closing card window will change back to board URL
$('#cardModal').on('hide.bs.modal', () => {
this.$router.push({name: 'board', params: {boardId: this.board.id}})
})
}

As you can see, we add two logics into this method. We call the loadInitial() method to load the data from the server when BoardPage is mounted, and we also bind to the close event of the card modal window to change the URL back to the board URL.

The following shows the loadInitial() method, as well as the loadCard() and loadBoard() methods:

methods: {
loadInitial () {
// The board page can be opened through a card URL.
if (this.$route.params.cardId) {
console.log('[BoardPage] Opened with card URL')
this.loadCard(this.$route.params.cardId).then(card => {
return this.loadBoard(card.boardId)
}).then(() => {
this.openCardWindow()
})
} else {
console.log('[BoardPage] Opened with board URL')
this.loadBoard(this.$route.params.boardId)
}
},
...
}

Inside the loadInitial() method, we check to see if it is a card URL or a board URL. When it is a card URL, we will call the loadCard() method first which returns a promise, and once the card is retrieved from the server, we use it to load the board. Once the board is loaded, we call the openCardWindow() method to show the card modal window. On the other hand, when it is a board URL, we load the board as we used to do, and in order to chain the actions, both the loadCard() method and loadBoard() method return a promise.

Now, let's take a look at the loadCard() method, which looks like the following:

methods: {
...
loadCard (cardId) {
return new Promise(resolve => {
console.log('[BoardPage] Loading card ' + cardId)
cardService.getCard(cardId).then(card => {
this.openedCard = card
resolve(card)
}).catch(error => {
notify.error(error.message)
})
})
},
...
}

As you can see, all this method does is call the getCard() method of cardService to retrieve the card information and assign it to the openedCard property of the component. This openedCard property is used to pass the card information to the CardModal.vue component that we will create.

..................Content has been hidden....................

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