Skip to content

Commit 96971b4

Browse files
committed
feature: allow pushing relative params (close #738)
1 parent 67e7b20 commit 96971b4

File tree

6 files changed

+111
-57
lines changed

6 files changed

+111
-57
lines changed

examples/nested-routes/app.js

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ new Vue({
9090
<li><router-link to="/parent/quy/123">/parent/quy</router-link></li>
9191
<li><router-link :to="{name: 'zap'}">/parent/zap</router-link></li>
9292
<li><router-link :to="{name: 'zap', params: {zapId: 1}}">/parent/zap/1</router-link></li>
93+
<li><router-link :to="{ params: { zapId: 2 }}">{ params: { zapId: 2 }} (relative params)</router-link></li>
9394
</ul>
9495
<router-view class="view"></router-view>
9596
</div>

src/create-matcher.js

+4-52
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,11 @@
11
/* @flow */
22

3-
import Regexp from 'path-to-regexp'
43
import { assert, warn } from './util/warn'
54
import { createRoute } from './util/route'
65
import { createRouteMap } from './create-route-map'
76
import { resolvePath } from './util/path'
87
import { normalizeLocation } from './util/location'
9-
10-
const regexpCache: {
11-
[key: string]: {
12-
keys: Array<?{ name: string }>,
13-
regexp: RegExp
14-
}
15-
} = Object.create(null)
16-
17-
const regexpParamsCache: {
18-
[key: string]: Array<string>
19-
} = Object.create(null)
20-
21-
const regexpCompileCache: {
22-
[key: string]: Function
23-
} = Object.create(null)
8+
import { getRouteRegex, fillParams } from './util/params'
249

2510
export function createMatcher (routes: Array<RouteConfig>): Matcher {
2611
const { pathMap, nameMap } = createRouteMap(routes)
@@ -35,10 +20,9 @@ export function createMatcher (routes: Array<RouteConfig>): Matcher {
3520

3621
if (name) {
3722
const record = nameMap[name]
38-
const paramNames = regexpParamsCache[record.path] ||
39-
(regexpParamsCache[record.path] = getRouteRegex(record.path).keys
40-
.filter(key => !key.optional)
41-
.map(key => key.name))
23+
const paramNames = getRouteRegex(record.path).keys
24+
.filter(key => !key.optional)
25+
.map(key => key.name)
4226

4327
if (typeof location.params !== 'object') {
4428
location.params = {}
@@ -158,22 +142,6 @@ export function createMatcher (routes: Array<RouteConfig>): Matcher {
158142
return match
159143
}
160144

161-
function getRouteRegex (path: string): Object {
162-
const hit = regexpCache[path]
163-
let keys, regexp
164-
165-
if (hit) {
166-
keys = hit.keys
167-
regexp = hit.regexp
168-
} else {
169-
keys = []
170-
regexp = Regexp(path, keys)
171-
regexpCache[path] = { keys, regexp }
172-
}
173-
174-
return { keys, regexp }
175-
}
176-
177145
function matchRoute (
178146
path: string,
179147
params: Object,
@@ -197,22 +165,6 @@ function matchRoute (
197165
return true
198166
}
199167

200-
function fillParams (
201-
path: string,
202-
params: ?Object,
203-
routeMsg: string
204-
): string {
205-
try {
206-
const filler =
207-
regexpCompileCache[path] ||
208-
(regexpCompileCache[path] = Regexp.compile(path))
209-
return filler(params || {}, { pretty: true })
210-
} catch (e) {
211-
assert(false, `missing param for ${routeMsg}: ${e.message}`)
212-
return ''
213-
}
214-
}
215-
216168
function resolveRecordPath (path: string, record: RouteRecord): string {
217169
return resolvePath(path, record.parent ? record.parent.path : '/', true)
218170
}

src/util/location.js

+28-1
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,37 @@
22

33
import { parsePath, resolvePath } from './path'
44
import { resolveQuery } from './query'
5+
import { fillParams } from './params'
6+
import { assert } from './warn'
57

68
export function normalizeLocation (
79
raw: RawLocation,
810
current?: Route,
911
append?: boolean
1012
): Location {
11-
const next: Location = typeof raw === 'string' ? { path: raw } : raw
13+
let next: Location = typeof raw === 'string' ? { path: raw } : raw
14+
// named target
1215
if (next.name || next._normalized) {
1316
return next
1417
}
1518

19+
// relative params
20+
if (!next.path && next.params && current) {
21+
next = assign({}, next)
22+
next._normalized = true
23+
const params: any = assign(assign({}, current.params), next.params)
24+
if (current.name) {
25+
next.name = current.name
26+
next.params = params
27+
} else if (current.matched) {
28+
const rawPath = current.matched[current.matched.length - 1].path
29+
next.path = fillParams(rawPath, params, `path ${current.path}`)
30+
} else {
31+
assert(false, `relative params navigation requires a current route.`)
32+
}
33+
return next
34+
}
35+
1636
const parsedPath = parsePath(next.path || '')
1737
const basePath = (current && current.path) || '/'
1838
const path = parsedPath.path
@@ -31,3 +51,10 @@ export function normalizeLocation (
3151
hash
3252
}
3353
}
54+
55+
function assign (a, b) {
56+
for (const key in b) {
57+
a[key] = b[key]
58+
}
59+
return a
60+
}

src/util/params.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* @flow */
2+
3+
import Regexp from 'path-to-regexp'
4+
import { assert } from './warn'
5+
6+
const regexpCache: {
7+
[key: string]: {
8+
keys: Array<?{ name: string }>,
9+
regexp: RegExp
10+
}
11+
} = Object.create(null)
12+
13+
export function getRouteRegex (path: string): Object {
14+
const hit = regexpCache[path]
15+
let keys, regexp
16+
17+
if (hit) {
18+
keys = hit.keys
19+
regexp = hit.regexp
20+
} else {
21+
keys = []
22+
regexp = Regexp(path, keys)
23+
regexpCache[path] = { keys, regexp }
24+
}
25+
26+
return { keys, regexp }
27+
}
28+
29+
const regexpCompileCache: {
30+
[key: string]: Function
31+
} = Object.create(null)
32+
33+
export function fillParams (
34+
path: string,
35+
params: ?Object,
36+
routeMsg: string
37+
): string {
38+
try {
39+
const filler =
40+
regexpCompileCache[path] ||
41+
(regexpCompileCache[path] = Regexp.compile(path))
42+
return filler(params || {}, { pretty: true })
43+
} catch (e) {
44+
assert(false, `missing param for ${routeMsg}: ${e.message}`)
45+
return ''
46+
}
47+
}

test/e2e/specs/nested-routes.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module.exports = {
33
browser
44
.url('http://localhost:8080/nested-routes/')
55
.waitForElementVisible('#app', 1000)
6-
.assert.count('li a', 8)
6+
.assert.count('li a', 9)
77
.assert.urlEquals('http://localhost:8080/nested-routes/parent')
88
.assert.containsText('.view', 'Parent')
99
.assert.containsText('.view', 'default')
@@ -43,7 +43,7 @@ module.exports = {
4343
return (
4444
JSON.stringify(params) === JSON.stringify(['quyId'])
4545
)
46-
}, null, '/')
46+
}, null, 'quyId')
4747

4848
.click('li:nth-child(8) a')
4949
.assert.urlEquals('http://localhost:8080/nested-routes/parent/zap/1')
@@ -52,7 +52,7 @@ module.exports = {
5252
.assert.evaluate(function () {
5353
var zapId = document.querySelector('pre').textContent
5454
return (zapId === '1')
55-
}, null, '/')
55+
}, null, 'zapId')
5656

5757
.click('li:nth-child(7) a')
5858
.assert.urlEquals('http://localhost:8080/nested-routes/parent/zap')
@@ -61,7 +61,14 @@ module.exports = {
6161
.assert.evaluate(function () {
6262
var zapId = document.querySelector('pre').textContent
6363
return (zapId === '')
64-
}, null, '/')
64+
}, null, 'optional zapId')
65+
66+
// test relative params
67+
.click('li:nth-child(9) a')
68+
.assert.evaluate(function () {
69+
var zapId = document.querySelector('pre').textContent
70+
return (zapId === '2')
71+
}, null, 'relative params')
6572

6673
// check initial visit
6774
.url('http://localhost:8080/nested-routes/parent/foo')

test/unit/specs/location.spec.js

+20
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,26 @@ describe('Location utils', () => {
6868
}))
6969
})
7070

71+
it('relative params (named)', () => {
72+
const loc = normalizeLocation({ params: { lang: 'fr' }}, {
73+
name: 'hello',
74+
params: { lang: 'en', id: 'foo' }
75+
})
76+
expect(loc._normalized).toBe(true)
77+
expect(loc.name).toBe('hello')
78+
expect(loc.params).toEqual({ lang: 'fr', id: 'foo' })
79+
})
80+
81+
it('relative params (non-named)', () => {
82+
const loc = normalizeLocation({ params: { lang: 'fr' }}, {
83+
path: '/en/foo',
84+
params: { lang: 'en', id: 'foo' },
85+
matched: [{ path: '/:lang/:id' }]
86+
})
87+
expect(loc._normalized).toBe(true)
88+
expect(loc.path).toBe('/fr/foo')
89+
})
90+
7191
it('object', () => {
7292
const loc = normalizeLocation({
7393
path: '/abc?foo=bar#hello',

0 commit comments

Comments
 (0)