Skip to content

Commit a1017e1

Browse files
committed
support beforeRouteEnter inside components
1 parent 0b6ff09 commit a1017e1

File tree

4 files changed

+66
-7
lines changed

4 files changed

+66
-7
lines changed

examples/navigation-guards/app.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,31 @@ const Baz = {
3939
}
4040
}
4141

42+
// Baz implements an in-component beforeRouteEnter hook
43+
const Qux = {
44+
data () {
45+
return {
46+
msg: null
47+
}
48+
},
49+
template: `<div>{{ msg }}</div>`,
50+
beforeRouteEnter (route, redirect, next) {
51+
// Note that enter hooks do not have access to `this`
52+
// because it is called before the component is even created.
53+
// However, we can provide a callback to `next` which will
54+
// receive the vm instance when the route has been confirmed.
55+
//
56+
// simulate an async data fetch.
57+
// this pattern is useful when you want to stay at current route
58+
// and only switch after the data has been fetched.
59+
setTimeout(() => {
60+
next(vm => {
61+
vm.msg = 'Qux'
62+
})
63+
}, 300)
64+
}
65+
}
66+
4267
const router = new VueRouter({
4368
mode: 'history',
4469
base: __dirname,
@@ -53,7 +78,10 @@ const router = new VueRouter({
5378
{ path: '/bar', component: Bar, meta: { needGuard: true }},
5479

5580
// Baz implements an in-component beforeRouteLeave hook
56-
{ path: '/baz', component: Baz }
81+
{ path: '/baz', component: Baz },
82+
83+
// Qux implements an in-component beforeRouteEnter hook
84+
{ path: '/qux', component: Qux }
5785
]
5886
})
5987

@@ -75,6 +103,7 @@ new Vue({
75103
<li><router-link to="/foo">/foo</router-link></li>
76104
<li><router-link to="/bar">/bar</router-link></li>
77105
<li><router-link to="/baz">/baz</router-link></li>
106+
<li><router-link to="/qux">/qux</router-link></li>
78107
</ul>
79108
<router-view class="view"></router-view>
80109
</div>

src/history/base.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,18 @@ export class History {
5151
activated
5252
} = resolveQueue(this.current.matched, route.matched)
5353

54+
const postEnterCbs = []
5455
const queue = [].concat(
55-
// deactivate guards
56+
// in-component leave guards
5657
extractLeaveGuards(deactivated),
5758
// global before hooks
5859
this.router.beforeHooks,
59-
// activate guards
60+
// enter guards
6061
activated.map(m => m.beforeEnter),
6162
// async components
62-
resolveAsyncComponents(activated)
63+
resolveAsyncComponents(activated),
64+
// in-component enter guards
65+
extractEnterGuards(activated, postEnterCbs)
6366
).filter(_ => _)
6467

6568
this.pending = route
@@ -72,6 +75,9 @@ export class History {
7275
if (isSameRoute(route, this.pending)) {
7376
this.pending = null
7477
cb(route)
78+
this.router.app.$nextTick(() => {
79+
postEnterCbs.forEach(cb => cb())
80+
})
7581
}
7682
}
7783
)
@@ -128,13 +134,29 @@ function extractLeaveGuards (matched: Array<RouteRecord>): Array<?Function> {
128134
return flatMapComponents(matched, (def, instance) => {
129135
const guard = def && def.beforeRouteLeave
130136
if (guard) {
131-
return function routeGuard () {
137+
return function routeLeaveGuard () {
132138
return guard.apply(instance, arguments)
133139
}
134140
}
135141
}).reverse()
136142
}
137143

144+
function extractEnterGuards (matched: Array<RouteRecord>, cbs: Array<Function>): Array<?Function> {
145+
return flatMapComponents(matched, (def, _, match, key) => {
146+
const guard = def && def.beforeRouteEnter
147+
if (guard) {
148+
return function routeEnterGuard (route, redirect, next) {
149+
return guard(route, redirect, cb => {
150+
next()
151+
cb && cbs.push(() => {
152+
cb(match.instances[key] && match.instances[key].child)
153+
})
154+
})
155+
}
156+
}
157+
})
158+
}
159+
138160
function resolveAsyncComponents (matched: Array<RouteRecord>): Array<?Function> {
139161
return flatMapComponents(matched, (def, _, match, key) => {
140162
// if it's a function and doesn't have Vue options attached,

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ export default class VueRouter {
4949
`before creating root instance.`
5050
)
5151

52+
this.app = app
53+
5254
const { mode, options, fallback } = this
5355
switch (mode) {
5456
case 'history':
@@ -64,7 +66,6 @@ export default class VueRouter {
6466
assert(false, `invalid mode: ${mode}`)
6567
}
6668

67-
this.app = app
6869
this.history.listen(route => {
6970
this.app._route = route
7071
})

test/e2e/specs/navigation-guards.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module.exports = {
88
browser
99
.url('http://localhost:8080/navigation-guards/')
1010
.waitForElementVisible('#app', 1000)
11-
.assert.count('li a', 4)
11+
.assert.count('li a', 5)
1212
.assert.containsText('.view', 'home')
1313

1414
.click('li:nth-child(2) a')
@@ -100,6 +100,13 @@ module.exports = {
100100
.acceptAlert()
101101
.assert.urlEquals('http://localhost:8080/navigation-guards/bar')
102102
.assert.containsText('.view', 'bar')
103+
104+
.click('li:nth-child(5) a')
105+
.assert.urlEquals('http://localhost:8080/navigation-guards/bar')
106+
.assert.containsText('.view', 'bar')
107+
.waitFor(300)
108+
.assert.urlEquals('http://localhost:8080/navigation-guards/qux')
109+
.assert.containsText('.view', 'Qux')
103110
.end()
104111
}
105112
}

0 commit comments

Comments
 (0)