Skip to content

Commit 24a0af2

Browse files
committed
Merge pull request #395 from dancannon/patch-1
[fixed] Added support for building URLs with optional parameters
2 parents d6b927a + 064e7c8 commit 24a0af2

File tree

3 files changed

+68
-7
lines changed

3 files changed

+68
-7
lines changed

modules/components/Routes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function findMatches(path, routes, defaultRoute, notFoundRoute) {
3232

3333
if (matches != null) {
3434
var rootParams = getRootMatch(matches).params;
35-
35+
3636
params = route.props.paramNames.reduce(function (params, paramName) {
3737
params[paramName] = rootParams[paramName];
3838
return params;

modules/utils/Path.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ function encodeURLPath(path) {
1515
}
1616

1717
var paramCompileMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|[*.()\[\]\\+|{}^$]/g;
18-
var paramInjectMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|[*]/g;
18+
var paramInjectMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$?]*[?]?)|[*]/g;
19+
var paramInjectTrailingSlashMatcher = /\/\/\?|\/\?/g;
1920
var queryMatcher = /\?(.+)/;
2021

2122
var _compiledPatterns = {};
@@ -86,10 +87,18 @@ var Path = {
8687
return pattern.replace(paramInjectMatcher, function (match, paramName) {
8788
paramName = paramName || 'splat';
8889

89-
invariant(
90-
params[paramName] != null,
91-
'Missing "' + paramName + '" parameter for path "' + pattern + '"'
92-
);
90+
// If param is optional don't check for existence
91+
if (paramName.slice(-1) !== '?') {
92+
invariant(
93+
params[paramName] != null,
94+
'Missing "' + paramName + '" parameter for path "' + pattern + '"'
95+
);
96+
} else {
97+
paramName = paramName.slice(0, -1)
98+
if (params[paramName] == null) {
99+
return '';
100+
}
101+
}
93102

94103
var segment;
95104
if (paramName === 'splat' && Array.isArray(params[paramName])) {
@@ -104,7 +113,7 @@ var Path = {
104113
}
105114

106115
return encodeURLPath(segment);
107-
});
116+
}).replace(paramInjectTrailingSlashMatcher, '/');
108117
},
109118

110119
/**

modules/utils/__tests__/Path-test.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,34 @@ describe('Path.extractParams', function () {
4747
});
4848
});
4949

50+
describe('and the pattern is optional', function () {
51+
var pattern = 'comments/:id?/edit'
52+
53+
describe('and the path matches with supplied param', function () {
54+
it('returns an object with the params', function () {
55+
expect(Path.extractParams(pattern, 'comments/123/edit')).toEqual({ id: '123' });
56+
});
57+
});
58+
describe('and the path matches without supplied param', function () {
59+
it('returns an object with param set to null', function () {
60+
expect(Path.extractParams(pattern, 'comments//edit')).toEqual({id: null});
61+
});
62+
});
63+
});
64+
describe('and the pattern and forward slash are optional', function () {
65+
var pattern = 'comments/:id?/?edit'
66+
67+
describe('and the path matches with supplied param', function () {
68+
it('returns an object with the params', function () {
69+
expect(Path.extractParams(pattern, 'comments/123/edit')).toEqual({ id: '123' });
70+
});
71+
});
72+
describe('and the path matches without supplied param', function () {
73+
it('returns an object with param set to null', function () {
74+
expect(Path.extractParams(pattern, 'comments/edit')).toEqual({id: null});
75+
});
76+
});
77+
});
5078
describe('and the path does not match', function () {
5179
it('returns null', function () {
5280
expect(Path.extractParams(pattern, 'users/123')).toBe(null);
@@ -166,6 +194,30 @@ describe('Path.injectParams', function () {
166194
});
167195
});
168196

197+
describe('and a param is optional', function () {
198+
var pattern = 'comments/:id?/edit';
199+
200+
it('returns the correct path when param is supplied', function () {
201+
expect(Path.injectParams(pattern, {id:'123'})).toEqual('comments/123/edit');
202+
});
203+
204+
it('returns the correct path when param is not supplied', function () {
205+
expect(Path.injectParams(pattern, {})).toEqual('comments//edit');
206+
});
207+
});
208+
209+
describe('and a param and forward slash are optional', function () {
210+
var pattern = 'comments/:id?/?edit';
211+
212+
it('returns the correct path when param is supplied', function () {
213+
expect(Path.injectParams(pattern, {id:'123'})).toEqual('comments/123/edit');
214+
});
215+
216+
it('returns the correct path when param is not supplied', function () {
217+
expect(Path.injectParams(pattern, {})).toEqual('comments/edit');
218+
});
219+
});
220+
169221
describe('and all params are present', function () {
170222
it('returns the correct path', function () {
171223
expect(Path.injectParams(pattern, { id: 'abc' })).toEqual('comments/abc/edit');

0 commit comments

Comments
 (0)