Skip to content

Commit db5125c

Browse files
committed
v-link & router.go(): support append: true for relative paths
1 parent 4c51083 commit db5125c

File tree

7 files changed

+74
-34
lines changed

7 files changed

+74
-34
lines changed

src/directives/link.js

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ export default function (Vue) {
2828
// don't redirect on right click
2929
if (e.button !== 0) return
3030

31+
let target = this.target
3132
if (this.el.tagName === 'A' || e.target === this.el) {
3233
// v-link on <a v-link="'path'">
3334
e.preventDefault()
34-
if (this.destination != null) {
35-
router.go(this.destination, this.replace === true)
35+
if (target != null) {
36+
router.go(target)
3637
}
3738
} else {
3839
// v-link delegate on <div v-link>
@@ -43,7 +44,11 @@ export default function (Vue) {
4344
if (!el || el.tagName !== 'A' || !el.href) return
4445
if (sameOrigin(el)) {
4546
e.preventDefault()
46-
router.go(el.pathname)
47+
router.go({
48+
path: el.pathname,
49+
replace: target && target.replace,
50+
append: target && target.append
51+
})
4752
}
4853
}
4954
}
@@ -57,22 +62,23 @@ export default function (Vue) {
5762

5863
update (path) {
5964
let router = this.vm.$route.router
60-
if (typeof path === 'object') {
61-
this.replace = path.replace
62-
this.exact = path.exactMatch
65+
let append
66+
this.target = path
67+
if (_.isObject(path)) {
68+
append = path.append
69+
this.exact = path.exact
6370
this.prevActiveClass = this.activeClass
6471
this.activeClass = path.activeClass
6572
}
66-
path = router._normalizePath(path)
67-
this.destination = path
73+
path = this.path = router._stringifyPath(path)
6874
this.activeRE = path && !this.exact
6975
? new RegExp('^' + path.replace(regexEscapeRE, '\\$&') + '(\\b|$)')
7076
: null
7177
this.updateClasses(this.vm.$route.path)
7278
let isAbsolute = path.charAt(0) === '/'
7379
// do not format non-hash relative paths
7480
let href = router.mode === 'hash' || isAbsolute
75-
? router.history.formatPath(path)
81+
? router.history.formatPath(path, append)
7682
: path
7783
if (this.el.tagName === 'A') {
7884
if (href) {
@@ -85,7 +91,7 @@ export default function (Vue) {
8591

8692
updateClasses (path) {
8793
let el = this.el
88-
let dest = this.destination
94+
let dest = this.path
8995
let router = this.vm.$route.router
9096
let activeClass = this.activeClass || router._linkActiveClass
9197
// clear old class

src/history/abstract.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ export default class AbstractHistory {
1515
// noop
1616
}
1717

18-
go (path) {
19-
path = this.currentPath = this.formatPath(path)
18+
go (path, replace, append) {
19+
path = this.currentPath = this.formatPath(path, append)
2020
this.onChange(path)
2121
}
2222

23-
formatPath (path) {
23+
formatPath (path, append) {
2424
return path.charAt(0) === '/'
2525
? path
26-
: resolvePath(this.currentPath, path)
26+
: resolvePath(this.currentPath, path, append)
2727
}
2828
}

src/history/hash.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,24 @@ export default class HashHistory {
3434
window.removeEventListener('hashchange', this.listener)
3535
}
3636

37-
go (path, replace) {
38-
path = this.formatPath(path)
37+
go (path, replace, append) {
38+
path = this.formatPath(path, append)
3939
if (replace) {
4040
location.replace(path)
4141
} else {
4242
location.hash = path
4343
}
4444
}
4545

46-
formatPath (path) {
46+
formatPath (path, append) {
4747
let isAbsoloute = path.charAt(0) === '/'
4848
let prefix = '#' + (this.hashbang ? '!' : '')
4949
return isAbsoloute
5050
? prefix + path
5151
: prefix + resolvePath(
5252
location.hash.replace(/^#!?/, ''),
53-
path
53+
path,
54+
append
5455
)
5556
}
5657
}

src/history/html5.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,8 @@ export default class HTML5History {
3737
window.removeEventListener('popstate', this.listener)
3838
}
3939

40-
go (path, replace) {
41-
let root = this.root
42-
let url = this.formatPath(path, root)
40+
go (path, replace, append) {
41+
let url = this.formatPath(path, append)
4342
if (replace) {
4443
history.replaceState({}, '', url)
4544
} else {
@@ -63,12 +62,12 @@ export default class HTML5History {
6362
this.onChange(path, null, hash)
6463
}
6564

66-
formatPath (path) {
65+
formatPath (path, append) {
6766
return path.charAt(0) === '/'
6867
// absolute path
6968
? this.root
7069
? this.root + '/' + path.replace(/^\//, '')
7170
: path
72-
: resolvePath(this.base || location.pathname, path)
71+
: resolvePath(this.base || location.pathname, path, append)
7372
}
7473
}

src/index.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,15 @@ class Router {
192192
* @param {Boolean} [replace]
193193
*/
194194

195-
go (path, replace) {
196-
path = this._normalizePath(path)
197-
this.history.go(path, replace)
195+
go (path) {
196+
let replace = false
197+
let append = false
198+
if (Vue.util.isObject(path)) {
199+
replace = path.replace
200+
append = path.append
201+
}
202+
path = this._stringifyPath(path)
203+
this.history.go(path, replace, append)
198204
}
199205

200206
/**
@@ -204,7 +210,7 @@ class Router {
204210
*/
205211

206212
replace (path) {
207-
this.go(path, true)
213+
this.go({ path, replace: true })
208214
}
209215

210216
/**
@@ -494,7 +500,7 @@ class Router {
494500
* @return {String}
495501
*/
496502

497-
_normalizePath (path) {
503+
_stringifyPath (path) {
498504
if (typeof path === 'object') {
499505
if (path.name) {
500506
var params = path.params || {}

src/util.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ export function warn (msg, err) {
2727
*
2828
* @param {String} base
2929
* @param {String} relative
30+
* @param {Boolean} append
3031
* @return {String}
3132
*/
3233

33-
export function resolvePath (base, relative) {
34+
export function resolvePath (base, relative, append) {
3435
let query = base.match(/(\?.*)$/)
3536
if (query) {
3637
query = query[1]
@@ -41,8 +42,10 @@ export function resolvePath (base, relative) {
4142
return base + relative
4243
}
4344
let stack = base.split('/')
44-
// remove trailing segment
45-
stack.pop()
45+
// remove trailing segment if not appending
46+
if (!append) {
47+
stack.pop()
48+
}
4649
// resolve relative path
4750
let segments = relative.split('/')
4851
for (let i = 0; i < segments.length; i++) {

test/unit/specs/core.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ describe('Core', function () {
4949
},
5050
'/b/:msg': {
5151
name: 'b',
52-
component: { template: 'B{{$route.params.msg}}{{$route.query.msg}}' }
52+
component: { template: 'B{{$route.params.msg}}{{$route.query.msg}}<router-view></router-view>' },
53+
subRoutes: {
54+
'/c': {
55+
component: { template: 'C' }
56+
}
57+
}
5358
}
5459
})
5560
var App = Vue.extend({
@@ -62,6 +67,8 @@ describe('Core', function () {
6267
// relative
6368
[{ path: '../a/A' }, 'AA'],
6469
[{ path: '../b/B' }, 'BB'],
70+
// relative with append: true
71+
[{ path: 'c', append: true }, 'BBC'],
6572
// named routes
6673
[{ name: 'a', params: {msg: 'A'}}, 'AA'],
6774
[{ name: 'b', params: {msg: 'B'}, query: {msg: 'B'}}, 'BBB']
@@ -184,6 +191,15 @@ describe('Core', function () {
184191
'<a id="link-c" v-link="{ path: c }"></c>' +
185192
'</div>'
186193
}
194+
},
195+
// test v-link with relative path + append
196+
'/c': {
197+
component: { template: '<a id="link-c" v-link="{ path: \'d\', append: true }">Link C</a><router-view></router-view>' },
198+
subRoutes: {
199+
'/d': {
200+
component: { template: '+D' }
201+
}
202+
}
187203
}
188204
})
189205
var App = Vue.extend({
@@ -207,7 +223,16 @@ describe('Core', function () {
207223
click(link)
208224
nextTick(function () {
209225
expect(el.textContent).toBe('Link A')
210-
done()
226+
router.go('/c')
227+
nextTick(function () {
228+
expect(el.textContent).toBe('Link C')
229+
var link = el.querySelector('#link-c')
230+
click(link)
231+
nextTick(function () {
232+
expect(el.textContent).toBe('Link C+D')
233+
done()
234+
})
235+
})
211236
})
212237
})
213238
})
@@ -226,7 +251,7 @@ describe('Core', function () {
226251
template:
227252
'<a id="link-a" v-link="{ path: \'/a\' }">Link A</a>' +
228253
'<a id="link-b" v-link="{ path: \'/b\', activeClass: className }">Link B</a>' +
229-
'<a id="link-c" v-link="{ path: \'/\', exactMatch: true }">Link C</a>' +
254+
'<a id="link-c" v-link="{ path: \'/\', exact: true }">Link C</a>' +
230255
'<router-view></router-view>'
231256
})
232257
router.start(App, el)

0 commit comments

Comments
 (0)