diff --git a/example/example.js b/example/example.js
index 6135ddbb4..7e4c5bcd0 100644
--- a/example/example.js
+++ b/example/example.js
@@ -99,4 +99,15 @@ router.redirect({
'/info': '/about'
})
+router.beforeEach(function (from, to) {
+ if (to.path === '/forbidden') {
+ alert('this route is forbidden by a global before hook')
+ return false
+ }
+})
+
+router.afterEach(function (from, to) {
+ console.log('global after')
+})
+
router.start(App, '#app')
\ No newline at end of file
diff --git a/example/index.html b/example/index.html
index 53b7137b2..6db068692 100644
--- a/example/index.html
+++ b/example/index.html
@@ -12,6 +12,7 @@
App Header
inbox
about
user
+ forbidden
diff --git a/src/index.js b/src/index.js
index 97f3af0e3..60c01cd9b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,5 +1,4 @@
var Recognizer = require('route-recognizer')
-var hasPushState = typeof history !== 'undefined' && history.pushState
var installed = false
var Vue
@@ -13,14 +12,28 @@ var Vue
*/
function Router (options) {
+ options = options || {}
+
+ // Vue instances
this.app = null
this._children = []
+
+ // route recognizer
this._recognizer = new Recognizer()
+
+ // state
this._started = false
- this._currentPath = null
- this._notFoundHandler = null
- this._root = null
- this._hasPushState = hasPushState
+ this._currentRoute = { path: '' }
+
+ // feature detection
+ this._hasPushState = typeof history !== 'undefined' && history.pushState
+
+ // global handler/hooks
+ this._notFoundHandler = options.notFound || null
+ this._beforeEachHook = options.beforeEach || null
+ this._afterEachHook = options.afterEach || null
+
+ // resolve root path
var root = options && options.root
if (root) {
// make sure there's the starting slash
@@ -29,9 +42,13 @@ function Router (options) {
}
// remove trailing slash
this._root = root.replace(/\/$/, '')
+ } else {
+ this._root = null
}
- this._hashbang = !(options && options.hashbang === false)
- this._pushstate = !!(hasPushState && options && options.pushstate)
+
+ // mode
+ this._hashbang = options.hashbang !== false
+ this._pushstate = !!(this._hasPushState && options.pushstate)
}
/**
@@ -109,6 +126,26 @@ p.redirect = function (map) {
// use another recognizer to recognize redirects
}
+/**
+ * Set global before hook.
+ *
+ * @param {Function} fn
+ */
+
+p.beforeEach = function (fn) {
+ this._beforeEachHook = fn
+}
+
+/**
+ * Set global after hook.
+ *
+ * @param {Function} fn
+ */
+
+p.afterEach = function (fn) {
+ this._afterEachHook = fn
+}
+
/**
* Navigate to a given path.
* The path is assumed to be already decoded, and will
@@ -140,6 +177,18 @@ p.go = function (path, options) {
}
}
+/**
+ * Short hand for replacing current path
+ *
+ * @param {String} path
+ */
+
+p.replace = function (path) {
+ this.go(path, {
+ replace: true
+ })
+}
+
/**
* Start the router.
*
@@ -273,10 +322,12 @@ p._addRoute = function (path, config, segments) {
*/
p._match = function (path) {
- if (path === this._currentPath) {
+
+ var currentRoute = this._currentRoute
+ if (this.app && path === currentRoute.path) {
return
}
- this._currentPath = path
+
// normalize against root
if (
this._pushstate &&
@@ -285,7 +336,9 @@ p._match = function (path) {
) {
path = path.slice(this._root.length)
}
+
var matched = this._recognizer.recognize(path)
+
// aggregate params
var params
if (matched) {
@@ -298,6 +351,7 @@ p._match = function (path) {
return prev
}, {})
}
+
// construct route context
var route = {
path: path,
@@ -307,7 +361,18 @@ p._match = function (path) {
_matchedCount: 0,
_router: this
}
+
+ // check gloal before hook
+ if (this._beforeEachHook) {
+ var res = this._beforeEachHook.call(null, currentRoute, route)
+ if (res === false) {
+ this.replace(currentRoute.path)
+ return
+ }
+ }
+
if (!this.app) {
+ // initial render
this.app = new this._appConstructor({
el: this._appContainer,
data: {
@@ -315,11 +380,19 @@ p._match = function (path) {
}
})
} else {
+ // route change
this.app.route = route
this._children.forEach(function (child) {
child.route = route
})
}
+
+ // check global after hook
+ if (this._afterEachHook) {
+ this._afterEachHook.call(null, currentRoute, route)
+ }
+
+ this._currentRoute = route
}
/**
diff --git a/src/view.js b/src/view.js
index fc43589d4..93d17e068 100644
--- a/src/view.js
+++ b/src/view.js
@@ -62,9 +62,7 @@ module.exports = function (Vue) {
if (route._router._hasPushState) {
history.back()
} else if (previousRoute) {
- route._router.go(previousRoute.path, {
- replace: true
- })
+ route._router.replace(previousRoute.path)
}
return
}