Skip to content

Commit

Permalink
New function $routeSegment.getSegmentUrl which can return URL for the…
Browse files Browse the repository at this point in the history
… given segment; a bunch of new useful filters
  • Loading branch information
artch committed May 14, 2014
1 parent ed11d58 commit 2b255db
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 10 deletions.
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,63 @@ Helper method for checking whether current route starts with the given string.
Helper method for checking whether current route contains the given string.
##### getSegmentUrl(segmentName, routeParams)
A method for reverse routing which can return the route URL for the specified segment name.
- *segmentName*
The name of a segment as defined in `when()`.
- *routeParams*
Route params hash to be put into route URL template. Standard `$routeParams` object is used first;
it is extended (overrided) with this provided object then.
```javascript
$routeSegment.getSegmentUrl('s1.home'); // -> '/section1'
$routeSegment.getSegmentUrl('s1.prefs'); // -> '/section1/prefs'
$routeSegment.getSegmentUrl('s1.itemInfo', {id: 123}); // -> '/section1/123'
$routeSegment.getSegmentUrl('s1.itemInfo.edit', {id: 123}); // -> '/section1/123/edit'
```
### Filters ###
##### routeSegmentEqualsTo
A wrapper for `$routeSegment.name == value`.
```html
<li ng-class="{active: ('s1' | routeSegmentEqualsTo)}">
```
##### routeSegmentStartsWith
A wrapper for `$routeSegment.startsWith(value)`.
```html
<li ng-class="{active: ('s1' | routeSegmentStartsWith)}">
```
##### routeSegmentContains
A wrapper for `$routeSegment.contains(value)`.
```html
<li ng-class="{active: ('s1' | routeSegmentContains)}">
```
##### routeSegmentParam
A wrapper for `$routeSegment.$routeParams[value]`.
```html
<li ng-class="{active: ('s1.itemInfo' | routeSegmentEqualsTo) && ('id' | routeSegmentParam) == 123}">
```
##### routeSegmentUrl
A wrapper for `$routeSegment.getSegmentUrl`.
```html
<a ng-href="{{ 's1.home' | routeSegmentUrl }}">
<a ng-href="{{ 's1.itemInfo.edit' | routeSegmentUrl: {id: 123} }}">
```
License
-------
Expand Down
91 changes: 82 additions & 9 deletions src/route-segment.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

(function(angular) {

angular.module( 'route-segment', [] ).provider( '$routeSegment',
var mod = angular.module( 'route-segment', [] );
mod.provider( '$routeSegment',
['$routeProvider', function($routeProvider) {

var $routeSegmentProvider = this;
Expand All @@ -26,7 +27,8 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
};

var segments = this.segments = {},
rootPointer = pointer(segments, null);
rootPointer = pointer(segments, null),
segmentRoutes = {};

function camelCase(name) {
return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) {
Expand Down Expand Up @@ -121,6 +123,7 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
*/
$routeSegmentProvider.when = function(route, name) {
$routeProvider.when(route, {segment: name});
segmentRoutes[name] = route;
return this;
};

Expand Down Expand Up @@ -180,6 +183,31 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
if(this.chain[i] && this.chain[i].name == val)
return true;
return false;
},

/**
* A method for reverse routing which can return the route URL for the specified segment name
* @param {string} segmentName The name of a segment as defined in `when()`
* @param {Object} routeParams Route params hash to be put into route URL template
*/
getSegmentUrl: function(segmentName, routeParams) {
var url, i, m;
if(!segmentRoutes[segmentName])
throw new Error('Can not get URL for segment with name `'+segmentName+'`');

routeParams = angular.extend({}, $routeParams, routeParams || {});

url = segmentRoutes[segmentName];
for(i in routeParams) {
var regexp = new RegExp('\:'+i+'[\*\?]?','g');
url = url.replace(regexp, routeParams[i]);
}
url = url.replace(/\/\:.*?\?/g, '');

if(m = url.match(/\/\:([^\/]*)/))
throw new Error('Route param `'+m[1]+'` is not specified for route `'+segmentRoutes[segmentName]+'`');

return url;
}
};

Expand Down Expand Up @@ -239,11 +267,8 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
})
})(i);
}

}



curSegmentPromise.then(function() {

// Removing redundant segment in case if new segment chain is shorter than old one
Expand All @@ -255,9 +280,6 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
updateSegment(i, null);
}
})



}
});

Expand Down Expand Up @@ -418,7 +440,58 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',

return $routeSegment;
}];
}])
}]);

/**
* Usage:
* <a ng-href="{{ 'index.list' | routeSegmentUrl }}">
* <a ng-href="{{ 'index.list.itemInfo' | routeSegmentUrl: {id: 123} }}">
*/
mod.filter('routeSegmentUrl', ['$routeSegment', function($routeSegment) {
return function(segmentName, params) {
return $routeSegment.getSegmentUrl(segmentName, params);
}
}]);

/**
* Usage:
* <li ng-class="{active: ('index.list' | routeSegmentEqualsTo)}">
*/
mod.filter('routeSegmentEqualsTo', ['$routeSegment', function($routeSegment) {
return function(value) {
return $routeSegment.name == value;
}
}]);

/**
* Usage:
* <li ng-class="{active: ('section1' | routeSegmentStartsWith)}">
*/
mod.filter('routeSegmentStartsWith', ['$routeSegment', function($routeSegment) {
return function(value) {
return $routeSegment.startsWith(value);
}
}]);

/**
* Usage:
* <li ng-class="{active: ('itemInfo' | routeSegmentContains)}">
*/
mod.filter('routeSegmentContains', ['$routeSegment', function($routeSegment) {
return function(value) {
return $routeSegment.contains(value);
}
}]);

/**
* Usage:
* <li ng-class="{active: ('index.list.itemInfo' | routeSegmentEqualsTo) && ('id' | routeSegmentParam) == 123}">
*/
mod.filter('routeSegmentParam', ['$routeSegment', function($routeSegment) {
return function(value) {
return $routeSegment.$routeParams[value];
}
}]);


})(angular);
108 changes: 107 additions & 1 deletion test/unit/route-segment.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -806,5 +806,111 @@ describe('route segment', function() {

})


describe('reverse routes', function() {

it('should get simple reverse route without params', function() {
var url = $routeSegment.getSegmentUrl('section-first');
expect(url).toBe('/1');
})

it('should get 2nd level route without params', function() {
var url = $routeSegment.getSegmentUrl('section2.section21');
expect(url).toBe('/2/X');
})

it('should get a route with the specified params', function() {
var url = $routeSegment.getSegmentUrl('section2.section23', {id: 'TEST'});
expect(url).toBe('/2/TEST');
})

it('should get a route with param using * mark', function() {
$routeSegmentProvider.when('/foo/:param*/bar', 'foo.bar');
var url = $routeSegment.getSegmentUrl('foo.bar', {param: 'TEST'});
expect(url).toBe('/foo/TEST/bar');
})

it('should get a route with an optional param using ? mark', function() {
$routeSegmentProvider.when('/foo/:param?/bar', 'foo.bar');
var url = $routeSegment.getSegmentUrl('foo.bar', {param: 'TEST'});
expect(url).toBe('/foo/TEST/bar');

url = $routeSegment.getSegmentUrl('foo.bar', {});
expect(url).toBe('/foo/bar');
})

it('should get a route using injected $routeParams', inject(function($routeParams) {
$routeParams.param1 = 'TEST1';
$routeParams.param2 = 'TEST2';

$routeSegmentProvider.when('/foo/:param1/bar', 'foo.bar');
$routeSegmentProvider.when('/foo/:param1/bar/:param2', 'foo.bar.baz');

var url = $routeSegment.getSegmentUrl('foo.bar');
expect(url).toBe('/foo/TEST1/bar');

url = $routeSegment.getSegmentUrl('foo.bar', {param1: 'OVERRIDED1'});
expect(url).toBe('/foo/OVERRIDED1/bar');

url = $routeSegment.getSegmentUrl('foo.bar', {param2: 'OVERRIDED1'});
expect(url).toBe('/foo/TEST1/bar');

url = $routeSegment.getSegmentUrl('foo.bar.baz', {param1: 'OVERRIDED1'});
expect(url).toBe('/foo/OVERRIDED1/bar/TEST2');
}));

it('should throw an error for unknown segment', function() {
expect(function() {
$routeSegment.getSegmentUrl('unknown-segment');
}).toThrow();
})

it('should throw an error when required params not specified', function() {
expect(function() {
$routeSegment.getSegmentUrl('section2.section23');
}).toThrow();
})
});

describe('filters', function() {

it('routeSegmentUrl', inject(function($filter) {
spyOn($routeSegment,'getSegmentUrl').andReturn('foo');
var params = {};

expect($filter('routeSegmentUrl')('URL', params)).toBe('foo');
expect($routeSegment.getSegmentUrl).toHaveBeenCalledWith('URL', params);
}))

it('routeSegmentEqualsTo', inject(function($filter) {
$location.path('/1');
$rootScope.$digest();
expect($filter('routeSegmentEqualsTo')('section-first')).toBe(true);

$location.path('/2/X');
$rootScope.$digest();
expect($filter('routeSegmentEqualsTo')('section-first')).toBe(false);
expect($filter('routeSegmentEqualsTo')('section2.section21')).toBe(true);
}))

it('routeSegmentEqualsTo', inject(function($filter) {
$location.path('/2/X');
$rootScope.$digest();
expect($filter('routeSegmentStartsWith')('section-first')).toBe(false);
expect($filter('routeSegmentStartsWith')('section2')).toBe(true);
expect($filter('routeSegmentStartsWith')('section2.section21')).toBe(true);
}))

it('routeSegmentEqualsTo', inject(function($filter) {
$location.path('/2/X');
$rootScope.$digest();
expect($filter('routeSegmentContains')('section-first')).toBe(false);
expect($filter('routeSegmentContains')('section2')).toBe(true);
expect($filter('routeSegmentContains')('section21')).toBe(true);
}))

it('routeSegmentParam', inject(function($filter) {
$routeSegment.$routeParams.qux = 'quux';
expect($filter('routeSegmentParam')('qux')).toBe('quux');
}))
})
})

0 comments on commit 2b255db

Please sign in to comment.