From 2b255db63b7273be9f0c75b19c464620835db9b9 Mon Sep 17 00:00:00 2001 From: Artem Chivchalov Date: Tue, 13 May 2014 23:19:02 +0400 Subject: [PATCH] New function $routeSegment.getSegmentUrl which can return URL for the given segment; a bunch of new useful filters --- README.md | 57 +++++++++++++++++ src/route-segment.js | 91 ++++++++++++++++++++++++--- test/unit/route-segment.spec.js | 108 +++++++++++++++++++++++++++++++- 3 files changed, 246 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ff1837a..a55ae4b 100644 --- a/README.md +++ b/README.md @@ -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 +
  • +``` + +##### routeSegmentStartsWith + +A wrapper for `$routeSegment.startsWith(value)`. +```html +
  • +``` + +##### routeSegmentContains + +A wrapper for `$routeSegment.contains(value)`. +```html +
  • +``` + +##### routeSegmentParam + +A wrapper for `$routeSegment.$routeParams[value]`. +```html +
  • +``` + +##### routeSegmentUrl + +A wrapper for `$routeSegment.getSegmentUrl`. +```html + + +``` License ------- diff --git a/src/route-segment.js b/src/route-segment.js index e3e1ece..7201f1c 100644 --- a/src/route-segment.js +++ b/src/route-segment.js @@ -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; @@ -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) { @@ -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; }; @@ -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; } }; @@ -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 @@ -255,9 +280,6 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment', updateSegment(i, null); } }) - - - } }); @@ -418,7 +440,58 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment', return $routeSegment; }]; -}]) +}]); + +/** + * Usage: + * + * + */ +mod.filter('routeSegmentUrl', ['$routeSegment', function($routeSegment) { + return function(segmentName, params) { + return $routeSegment.getSegmentUrl(segmentName, params); + } +}]); + +/** + * Usage: + *
  • + */ +mod.filter('routeSegmentEqualsTo', ['$routeSegment', function($routeSegment) { + return function(value) { + return $routeSegment.name == value; + } +}]); + +/** + * Usage: + *
  • + */ +mod.filter('routeSegmentStartsWith', ['$routeSegment', function($routeSegment) { + return function(value) { + return $routeSegment.startsWith(value); + } +}]); + +/** + * Usage: + *
  • + */ +mod.filter('routeSegmentContains', ['$routeSegment', function($routeSegment) { + return function(value) { + return $routeSegment.contains(value); + } +}]); + +/** + * Usage: + *
  • + */ +mod.filter('routeSegmentParam', ['$routeSegment', function($routeSegment) { + return function(value) { + return $routeSegment.$routeParams[value]; + } +}]); })(angular); \ No newline at end of file diff --git a/test/unit/route-segment.spec.js b/test/unit/route-segment.spec.js index 8d64cb2..904d0b0 100644 --- a/test/unit/route-segment.spec.js +++ b/test/unit/route-segment.spec.js @@ -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'); + })) + }) }) \ No newline at end of file