Skip to content

Commit 083fae1

Browse files
committed
refactor v-link
- inherit current tansition params when navigating to a named route - improve active class matching for named routes with incomplete params
1 parent 64bb659 commit 083fae1

File tree

2 files changed

+109
-89
lines changed

2 files changed

+109
-89
lines changed

src/directives/link.js

Lines changed: 104 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,26 @@ const queryStringRE = /\?.*$/
77
// HTML5 history mode
88
export default function (Vue) {
99

10-
const _ = Vue.util
1110
const urlParser = document.createElement('a')
11+
const {
12+
bind,
13+
isObject,
14+
addClass,
15+
removeClass
16+
} = Vue.util
1217

1318
Vue.directive('link', {
1419

1520
bind () {
16-
let vm = this.vm
21+
const vm = this.vm
1722
/* istanbul ignore if */
1823
if (!vm.$route) {
19-
warn(
20-
'v-link can only be used inside a ' +
21-
'router-enabled app.'
22-
)
24+
warn('v-link can only be used inside a router-enabled app.')
2325
return
2426
}
27+
this.router = vm.$route.router
28+
// update things when the route changes
29+
this.unwatch = vm.$watch('$route', bind(this.onRouteUpdate, this))
2530
// no need to handle click if link expects to be opened
2631
// in a new window/tab.
2732
/* istanbul ignore if */
@@ -30,83 +35,99 @@ export default function (Vue) {
3035
return
3136
}
3237
// handle click
33-
let router = vm.$route.router
34-
this.handler = (e) => {
35-
// don't redirect with control keys
36-
if (e.metaKey || e.ctrlKey || e.shiftKey) return
37-
// don't redirect when preventDefault called
38-
if (e.defaultPrevented) return
39-
// don't redirect on right click
40-
if (e.button !== 0) return
38+
this.el.addEventListener('click', bind(this.onClick, this))
39+
},
40+
41+
update (target) {
42+
this.target = target
43+
if (isObject(target)) {
44+
this.append = target.append
45+
this.exact = target.exact
46+
this.prevActiveClass = this.activeClass
47+
this.activeClass = target.activeClass
48+
}
49+
this.onRouteUpdate(this.vm.$route)
50+
},
4151

42-
const target = this.target
43-
const go = (target) => {
44-
e.preventDefault()
45-
if (target != null) {
46-
router.go(target)
47-
}
52+
onClick (e) {
53+
// don't redirect with control keys
54+
if (e.metaKey || e.ctrlKey || e.shiftKey) return
55+
// don't redirect when preventDefault called
56+
if (e.defaultPrevented) return
57+
// don't redirect on right click
58+
if (e.button !== 0) return
59+
60+
const target = this.target
61+
const go = (target) => {
62+
e.preventDefault()
63+
if (target != null) {
64+
this.router.go(target)
4865
}
66+
}
4967

50-
if (this.el.tagName === 'A' || e.target === this.el) {
51-
// v-link on <a v-link="'path'">
52-
if (sameOrigin(this.el, target)) {
53-
go(target)
54-
} else if (typeof target === 'string') {
55-
window.location.href = target
56-
}
57-
} else {
58-
// v-link delegate on <div v-link>
59-
var el = e.target
60-
while (el && el.tagName !== 'A' && el !== this.el) {
61-
el = el.parentNode
62-
}
63-
if (!el) return
64-
if (!sameOrigin(el, target) && typeof target === 'string') {
65-
window.location.href = target
66-
}
67-
if (el.tagName !== 'A' || !el.href) {
68-
// allow not anchor
69-
go(target)
70-
} else if (sameOrigin(el, target)) {
71-
go({
72-
path: el.pathname,
73-
replace: target && target.replace,
74-
append: target && target.append
75-
})
76-
}
68+
if (this.el.tagName === 'A' || e.target === this.el) {
69+
// v-link on <a v-link="'path'">
70+
if (sameOrigin(this.el, target)) {
71+
go(target)
72+
} else if (typeof target === 'string') {
73+
window.location.href = target
74+
}
75+
} else {
76+
// v-link delegate on <div v-link>
77+
var el = e.target
78+
while (el && el.tagName !== 'A' && el !== this.el) {
79+
el = el.parentNode
80+
}
81+
if (!el) return
82+
if (!sameOrigin(el, target) && typeof target === 'string') {
83+
window.location.href = target
84+
}
85+
if (el.tagName !== 'A' || !el.href) {
86+
// allow not anchor
87+
go(target)
88+
} else if (sameOrigin(el, target)) {
89+
go({
90+
path: el.pathname,
91+
replace: target && target.replace,
92+
append: target && target.append
93+
})
7794
}
7895
}
79-
this.el.addEventListener('click', this.handler)
80-
// manage active link class
81-
this.unwatch = vm.$watch(
82-
'$route.path',
83-
_.bind(this.updateClasses, this)
84-
)
8596
},
8697

87-
update (path) {
88-
let router = this.vm.$route.router
89-
let append
90-
this.target = path
91-
if (_.isObject(path)) {
92-
append = path.append
93-
this.exact = path.exact
94-
this.prevActiveClass = this.activeClass
95-
this.activeClass = path.activeClass
98+
onRouteUpdate (route) {
99+
// router._stringifyPath is dependent on current route
100+
// and needs to be called again whenver route changes.
101+
var newPath = this.router._stringifyPath(this.target)
102+
if (this.path !== newPath) {
103+
this.path = newPath
104+
this.updateActiveMatch()
105+
this.updateHref()
96106
}
97-
path = this.path = router._stringifyPath(path)
98-
this.activeRE = path && !this.exact
107+
this.updateClasses(route.path)
108+
},
109+
110+
updateActiveMatch () {
111+
this.activeRE = this.path && !this.exact
99112
? new RegExp(
100113
'^' +
101-
path.replace(/\/$/, '').replace(regexEscapeRE, '\\$&') +
114+
this.path.replace(/\/$/, '').replace(regexEscapeRE, '\\$&') +
102115
'(\\/|$)'
103116
)
104117
: null
105-
this.updateClasses(this.vm.$route.path)
106-
let isAbsolute = path.charAt(0) === '/'
118+
},
119+
120+
updateHref () {
121+
if (this.target && this.target.name) {
122+
this.el.href = '#' + this.target.name
123+
return
124+
}
125+
const path = this.path
126+
const router = this.router
127+
const isAbsolute = path.charAt(0) === '/'
107128
// do not format non-hash relative paths
108-
let href = path && (router.mode === 'hash' || isAbsolute)
109-
? router.history.formatPath(path, append)
129+
const href = path && (router.mode === 'hash' || isAbsolute)
130+
? router.history.formatPath(path, this.append)
110131
: path
111132
if (this.el.tagName === 'A') {
112133
if (href) {
@@ -118,36 +139,31 @@ export default function (Vue) {
118139
},
119140

120141
updateClasses (path) {
121-
let el = this.el
122-
let router = this.vm.$route.router
123-
let activeClass = this.activeClass || router._linkActiveClass
142+
const el = this.el
143+
const activeClass = this.activeClass || this.router._linkActiveClass
124144
// clear old class
125145
if (this.prevActiveClass !== activeClass) {
126-
_.removeClass(el, this.prevActiveClass)
146+
removeClass(el, this.prevActiveClass)
127147
}
128148
// remove query string before matching
129-
let dest = this.path.replace(queryStringRE, '')
149+
const dest = this.path.replace(queryStringRE, '')
130150
path = path.replace(queryStringRE, '')
131151
// add new class
132152
if (this.exact) {
133-
if (
134-
dest === path ||
135-
(
136-
// also allow additional trailing slash
137-
dest.charAt(dest.length - 1) !== '/' &&
138-
dest === path.replace(trailingSlashRE, '')
139-
)
140-
) {
141-
_.addClass(el, activeClass)
153+
if (dest === path || (
154+
// also allow additional trailing slash
155+
dest.charAt(dest.length - 1) !== '/' &&
156+
dest === path.replace(trailingSlashRE, '')
157+
)) {
158+
addClass(el, activeClass)
142159
} else {
143-
_.removeClass(el, activeClass)
160+
removeClass(el, activeClass)
144161
}
145162
} else {
146-
if (this.activeRE &&
147-
this.activeRE.test(path)) {
148-
_.addClass(el, activeClass)
163+
if (this.activeRE && this.activeRE.test(path)) {
164+
addClass(el, activeClass)
149165
} else {
150-
_.removeClass(el, activeClass)
166+
removeClass(el, activeClass)
151167
}
152168
}
153169
},

src/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,11 @@ class Router {
544544
_stringifyPath (path) {
545545
if (path && typeof path === 'object') {
546546
if (path.name) {
547-
const params = path.params || {}
547+
const extend = Vue.util.extend
548+
const currentParams = this._currentTransition.to.params
549+
const params = currentParams
550+
? extend(extend({}, currentParams), path.params)
551+
: path.params || {}
548552
if (path.query) {
549553
params.queryParams = path.query
550554
}

0 commit comments

Comments
 (0)