NavigationFailureType | Vue Router - Navigation Error Handling ๐ซ โ
Comprehensive error management - Understand and handle navigation failures with precise error types!
๐ฏ Overview โ
NavigationFailureType is an enumeration that provides specific error types for navigation failures in Vue Router. These types help you identify exactly what went wrong during navigation attempts, enabling precise error handling and user feedback.
๐ Key Benefits โ
- Precise Error Identification: Know exactly why navigation failed
- Targeted Error Handling: Implement specific recovery strategies
- Better User Experience: Provide meaningful error messages
- Debugging Support: Simplify navigation issue troubleshooting
๐ก When to Use NavigationFailureType โ
๐จ Navigation Error Scenarios โ
User Permission Issues
- Authentication failures
- Route access restrictions
- Role-based navigation blocking
Application State Conflicts
- Form data loss prevention
- Unsaved changes protection
- Concurrent navigation attempts
๐ง Basic Usage โ
import { NavigationFailureType, isNavigationFailure } from 'vue-router'
// Check for specific failure types
router.push('/protected-route').catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.ABORTED)) {
console.log('Navigation was aborted')
}
if (isNavigationFailure(failure, NavigationFailureType.CANCELLED)) {
console.log('Navigation was cancelled')
}
})2
3
4
5
6
7
8
9
10
11
12
๐ Enumeration Values โ
NavigationFailureType.ABORTED โ
Description: Navigation was aborted by a navigation guard
Common Scenarios:
beforeEachguard returnsfalsebeforeResolveguard returnsfalse- Programmatic navigation guard rejection
Example Usage:
router.beforeEach((to, from, next) => {
if (!user.isAuthenticated) {
next(false) // This will cause ABORTED failure
} else {
next()
}
})
// Handling the failure
router.push('/dashboard').catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.ABORTED)) {
// Redirect to login or show error
router.push('/login')
}
})2
3
4
5
6
7
8
9
10
11
12
13
14
15
NavigationFailureType.CANCELLED โ
Description: A new navigation took place before the previous one finished
Common Scenarios:
- Rapid consecutive navigation calls
- User clicking multiple links quickly
- Programmatic navigation race conditions
Example Usage:
// Simulate rapid navigation
const rapidNavigation = async () => {
try {
// These will race and one will be CANCELLED
await Promise.all([
router.push('/page1'),
router.push('/page2'),
router.push('/page3')
])
} catch (failure) {
if (isNavigationFailure(failure, NavigationFailureType.CANCELLED)) {
console.log('Navigation was cancelled due to concurrent navigation')
// This is usually not an error - it's expected behavior
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
NavigationFailureType.DUPLICATED โ
Description: Navigation was prevented because the target location is the same as current location
Common Scenarios:
- Navigating to the same route with same parameters
- Programmatic navigation to current location
- User clicking active link
Example Usage:
// Prevent duplicate navigation
const smartNavigate = (route) => {
return router.push(route).catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.DUPLICATED)) {
// This is usually not an error - just ignore or log
console.log('Already at target location')
return Promise.resolve() // Resolve gracefully
}
throw failure // Re-throw other errors
})
}
// Usage
smartNavigate('/current-location') // Will be DUPLICATED2
3
4
5
6
7
8
9
10
11
12
13
14
๐ง Advanced Techniques โ
Example 1: Comprehensive Navigation Error Handler โ
// utils/navigationErrorHandler.ts
import {
NavigationFailureType,
isNavigationFailure,
NavigationFailure
} from 'vue-router'
export class NavigationErrorHandler {
static handle(failure: NavigationFailure, to: RouteLocationRaw) {
if (isNavigationFailure(failure, NavigationFailureType.ABORTED)) {
return this.handleAborted(failure, to)
}
if (isNavigationFailure(failure, NavigationFailureType.CANCELLED)) {
return this.handleCancelled(failure, to)
}
if (isNavigationFailure(failure, NavigationFailureType.DUPLICATED)) {
return this.handleDuplicated(failure, to)
}
// Unknown failure type
return this.handleUnknown(failure, to)
}
private static handleAborted(failure: NavigationFailure, to: RouteLocationRaw) {
console.warn('Navigation aborted by guard:', to)
// Check if it's an authentication issue
if (this.isAuthenticationError(to)) {
this.redirectToLogin(to)
return
}
// Check for permission issues
if (this.isPermissionError(to)) {
this.showPermissionError()
return
}
// Generic abort handling
this.showGenericError('Navigation was blocked')
}
private static handleCancelled(failure: NavigationFailure, to: RouteLocationRaw) {
// Usually not an error - just log for debugging
console.log('Navigation cancelled (likely concurrent navigation):', to)
// In some cases, you might want to retry
if (this.shouldRetry(to)) {
setTimeout(() => router.push(to), 100)
}
}
private static handleDuplicated(failure: NavigationFailure, to: RouteLocationRaw) {
// Not an error - just confirm we're already there
console.log('Already at target location:', to)
// Optional: Scroll to top or refresh data
if (this.shouldRefreshData(to)) {
this.refreshCurrentPageData()
}
}
private static handleUnknown(failure: NavigationFailure, to: RouteLocationRaw) {
console.error('Unknown navigation failure:', failure, to)
this.showGenericError('Navigation failed unexpectedly')
}
// Helper methods
private static isAuthenticationError(to: RouteLocationRaw): boolean {
// Check if route requires authentication
const route = typeof to === 'string' ? { path: to } : to
return route.meta?.requiresAuth === true
}
private static isPermissionError(to: RouteLocationRaw): boolean {
// Check route permissions
const route = typeof to === 'string' ? { path: to } : to
return route.meta?.requiredPermissions !== undefined
}
private static redirectToLogin(to: RouteLocationRaw) {
router.push({
path: '/login',
query: { redirect: typeof to === 'string' ? to : to.path }
})
}
private static showPermissionError() {
// Show permission error modal or message
alert('You do not have permission to access this page')
}
private static showGenericError(message: string) {
// Show generic error to user
alert(message)
}
private static shouldRetry(to: RouteLocationRaw): boolean {
// Only retry for important navigations
return to.toString().includes('/checkout') ||
to.toString().includes('/payment')
}
private static shouldRefreshData(to: RouteLocationRaw): boolean {
// Refresh data when navigating to same dynamic route
return typeof to !== 'string' && to.params !== undefined
}
private static refreshCurrentPageData() {
// Emit event or trigger data refresh
window.dispatchEvent(new CustomEvent('refresh-page-data'))
}
}
// Usage
router.push('/protected-route').catch(failure => {
NavigationErrorHandler.handle(failure, '/protected-route')
})2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
Example 2: Navigation Guard with Detailed Error Reporting โ
// guards/navigationGuard.ts
import { NavigationFailureType, isNavigationFailure } from 'vue-router'
import { useUserStore } from '@/stores/user'
import { useNotificationStore } from '@/stores/notification'
export function setupNavigationGuards(router) {
// Global navigation guard with error handling
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
const notificationStore = useNotificationStore()
// Check authentication
if (to.meta.requiresAuth && !userStore.isAuthenticated) {
notificationStore.showWarning('Please log in to access this page')
next({
path: '/login',
query: { redirect: to.fullPath }
})
return
}
// Check permissions
if (to.meta.requiredPermissions) {
const hasPermission = userStore.hasPermissions(to.meta.requiredPermissions)
if (!hasPermission) {
notificationStore.showError('Insufficient permissions')
next(false) // This will cause ABORTED failure
return
}
}
// Check for unsaved changes
if (from.meta.requiresSaveConfirmation && hasUnsavedChanges()) {
if (confirm('You have unsaved changes. Are you sure you want to leave?')) {
clearUnsavedChanges()
next()
} else {
next(false) // ABORTED due to user cancellation
}
return
}
// All checks passed
next()
})
// Global error handler for navigation failures
router.onError((error) => {
if (isNavigationFailure(error, NavigationFailureType.ABORTED)) {
// Already handled by guards - just log for debugging
console.debug('Navigation aborted by guard')
} else if (isNavigationFailure(error, NavigationFailureType.CANCELLED)) {
console.debug('Navigation cancelled - concurrent navigation detected')
} else {
// Log unexpected errors
console.error('Unexpected navigation error:', error)
notificationStore.showError('Navigation failed. Please try again.')
}
})
}
// Utility function to check for unsaved changes
function hasUnsavedChanges(): boolean {
// Implement based on your application state
return false
}
function clearUnsavedChanges() {
// Clear the unsaved changes state
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
๐ ๏ธ Practical Examples โ
Example 3: E-commerce Checkout Flow with Error Handling โ
// features/checkout/navigation.ts
import { NavigationFailureType, isNavigationFailure } from 'vue-router'
export class CheckoutNavigation {
static async navigateToStep(step: string): Promise<boolean> {
try {
await router.push(`/checkout/${step}`)
return true
} catch (failure) {
if (isNavigationFailure(failure, NavigationFailureType.ABORTED)) {
this.handleCheckoutAborted(step)
return false
}
if (isNavigationFailure(failure, NavigationFailureType.CANCELLED)) {
// Retry after short delay
await new Promise(resolve => setTimeout(resolve, 100))
return this.navigateToStep(step)
}
// Other errors
console.error('Checkout navigation error:', failure)
this.showCheckoutError()
return false
}
}
private static handleCheckoutAborted(step: string) {
switch (step) {
case 'shipping':
this.showShippingError()
break
case 'payment':
this.showPaymentError()
break
case 'review':
this.showReviewError()
break
default:
this.showGenericCheckoutError()
}
}
private static showShippingError() {
alert('Please complete your shipping information before proceeding')
}
private static showPaymentError() {
alert('Payment information is incomplete or invalid')
}
private static showReviewError() {
alert('Please review your order before completing the purchase')
}
private static showGenericCheckoutError() {
alert('Unable to proceed with checkout. Please try again.')
}
private static showCheckoutError() {
alert('Checkout navigation failed. Please refresh the page and try again.')
}
}
// Usage in checkout component
const proceedToPayment = async () => {
const success = await CheckoutNavigation.navigateToStep('payment')
if (success) {
// Update UI state
setCurrentStep('payment')
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
Example 4: Admin Dashboard with Permission-Based Navigation โ
// features/admin/navigation.ts
import { NavigationFailureType, isNavigationFailure } from 'vue-router'
export class AdminNavigation {
static async navigateToAdminSection(section: string): Promise<boolean> {
const targetRoute = `/admin/${section}`
try {
await router.push(targetRoute)
return true
} catch (failure) {
if (isNavigationFailure(failure, NavigationFailureType.ABORTED)) {
await this.handleAdminAccessDenied(section)
return false
}
if (isNavigationFailure(failure, NavigationFailureType.DUPLICATED)) {
// Already in the section - refresh data
this.refreshAdminSectionData(section)
return true
}
// Log other errors
console.error('Admin navigation error:', failure)
return false
}
}
private static async handleAdminAccessDenied(section: string) {
const userStore = useUserStore()
if (!userStore.isAuthenticated) {
// Redirect to login
await router.push({
path: '/login',
query: { redirect: `/admin/${section}` }
})
} else if (!userStore.isAdmin) {
// Show permission error
this.showAdminPermissionError()
} else {
// Generic admin error
this.showAdminAccessError()
}
}
private static showAdminPermissionError() {
alert('Administrator privileges required to access this section')
}
private static showAdminAccessError() {
alert('Unable to access admin section. Please contact support.')
}
private static refreshAdminSectionData(section: string) {
// Emit refresh event for the specific section
window.dispatchEvent(new CustomEvent(`admin-${section}-refresh`))
}
}
// Usage in admin component
const navigateToUsers = async () => {
const success = await AdminNavigation.navigateToAdminSection('users')
if (success) {
console.log('Successfully navigated to users section')
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
๐ TypeScript Integration โ
Type-Safe Navigation Failure Handling โ
// types/navigation.ts
import type { NavigationFailure } from 'vue-router'
export interface NavigationResult {
success: boolean
failure?: NavigationFailure
failureType?: NavigationFailureType
}
export async function safeNavigate(to: RouteLocationRaw): Promise<NavigationResult> {
try {
await router.push(to)
return { success: true }
} catch (failure) {
if (isNavigationFailure(failure)) {
return {
success: false,
failure,
failureType: failure.type
}
}
// Non-navigation failure
return {
success: false,
failure: failure as NavigationFailure
}
}
}
// Usage
const result = await safeNavigate('/protected-route')
if (!result.success && result.failureType === NavigationFailureType.ABORTED) {
// Handle aborted navigation
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
๐ Best Practices โ
โ Do โ
- Handle All Failure Types: Implement specific handling for each type
- Provide User Feedback: Inform users about navigation issues
- Log for Debugging: Keep logs for troubleshooting
- Use TypeScript: Leverage type safety for error handling
โ Don't โ
- Ignore Cancelled Navigations: They're usually not errors
- Over-Report to Users: Avoid confusing users with technical details
- Forget Error Recovery: Implement retry logic where appropriate
- Neglect Edge Cases: Handle all possible navigation scenarios
๐ Related Features โ
- Navigation Guards: Primary source of navigation failures
- Route Meta Fields: Store navigation requirements and permissions
- Router Error Handling: Global error handling mechanisms
- Programmatic Navigation: Methods that can return failures
๐ก Pro Tip
Combine NavigationFailureType with Vue Router's navigation guards to create sophisticated permission systems that provide clear feedback to users when navigation is blocked due to authentication or authorization issues!
Ready for robust navigation error handling? Start implementing precise error management with NavigationFailureType to create reliable, user-friendly navigation experiences in your Vue.js applications! ๐ซ