Monday, May 20, 2024
 Popular · Latest · Hot · Upcoming
134
rated 0 times [  137] [ 3]  / answers: 1 / hits: 30206  / 11 Years ago, sat, april 27, 2013, 12:00:00

How can I pass actual URL (with slashes, commas, etc.) as a $routeParam to AngularJS App?



this will work:
http://paprikka.github.io/le-bat/#/preview/asdadasda



this won't:
http://paprikka.github.io/le-bat/#/preview/http://page.com



neither will this:
http://paprikka.github.io/le-bat/#/preview/http%3A%2F%2Fpage.com



or this:
http://paprikka.github.io/le-bat/#/preview/?url=http%3A%2F%2Fpage.com



Details



AngularJS routing mechanism by its design does not allow to pass strings with slashes as query parameters. I can understand the reasoning behind this decision - we don't want to create a stateless server here.



However, there are still cases when using different separators or regular expressions in routes might be necessary.



I wanted to create an app that takes a url hash string parameter and loads its content to an iframe (link here). Routes are set up in pretty standard way (I'm using Coffeescript, but this snippet does not differ from pure js):



$routeProvider
.when('/preview/:src', {templateUrl: 'partials/preview.html',
controller: 'PreviewCtrl'})
.when('/preview', {templateUrl: 'partials/preview.html',
controller: 'PreviewCtrl'})


Of course, I can load url from hash before AngularJS gets bootstrapped and then pass it to the library, but it would be nice if I could also update current route parameter when changing data in scope - that's why I think it's much better not to avoid AngularJS API.


More From » angularjs

 Answers
16

Ok, I've managed to find a solution working with current stable version (@1.0.7).



Current way of handling this problem will involve $route-related events, parsing angular-incompatible urls on the fly and handling them via an additional service working in a similar way as $http interception.



You can see working code examples here: http://embed.plnkr.co/fIA2xj/preview



Main steps




  1. pass an angular-incompatible url as usual, eg. go to site.com/url/http://site.com

  2. listen to a $routeChangeStart event and extract correct url parameter for paths beginning with /url/

  3. encode the correct url parameter to an angular-compatible form (in this particular case, I use base64). Don't use encodeURIComponent, because angular will treat as any other url

  4. redirect to another route with your business logic, eg. site.com/parsed-url/BASE64_GOES_HERE

  5. decode the URL in the controller and use it as usual :)



Code



Create angular app module as usual



angular.module('routes',[]).config([

'$routeProvider',

function($routeProvider){
$routeProvider
.when('/test', {templateUrl: 'test.html'})
// This one is important:
// We define a route that will be used internally and handle
// parameters with urls parsed by us via the URLInterceptor service
.when('/parsed-url/:url', {templateUrl: 'url.html', controller:'URLCtrl'})
.when('/', {redirectTo: '/test'})
.otherwise({templateUrl: '404.html'});

}

])


URL Interceptor service (singleton)



.service('URLInterceptor', function($rootScope, $location){
// We listen to $routeChangeStart event and intercept it if
// the path matches our url scheme. In this case, every route
// beginning with /url/ will be caught
$rootScope.$on('$routeChangeStart', function(e, next, current){

// $location.path does change BEFORE actual routing happens,
// so in this case we get parsed new location object
// for free.

// To be hones, a better way of handling this case might be using
// $locationChangeStart event instead, but it would require us to parse urls
// manually.
var path = $location.path();
// check if string begins with '/url/'
var matcher = path.slice(0,5);
var cleanPath = '';
if (matcher === '/url/'){
// Yes it does, yay!
// Remove leading '/url/' to extract the actual parameter
cleanPath = path.slice(5);
// Encode our url to a safe version. We know that encodeURIComponent won't
// work either, so a good choice might be base64.
// I'm using https://code.google.com/p/javascriptbase64/downloads
$location.path('/parsed-url/' + Base64.encode(cleanPath));
// Prevent default event execution. Note that, it won't cancel related $location Events
e.preventDefault();
}
});

return {
decode: Base64.decode,
encode: Base64.encode
}
})


Controllers



// Main application controller
// We instantiate our URLInterceptor service here
.controller('AppCtrl',function($scope, $location, URLInterceptor){
$scope.navigateTo = function (path) {
$location.path('/url/' + path);
}
})
.controller('URLCtrl', function($scope, $routeParams, URLInterceptor){
$scope.url = URLInterceptor.decode($routeParams.url);
});


Two things you should remember:




  1. Although I tried to create a solution as clean as possible, usually passing the data this way to angular isn't considered a good practice, so try not to use it unless you really need to.

  2. You can handle this issue with only one route. I just find it cleaner this way.


[#78566] Friday, April 26, 2013, 11 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
isaacvalentinn

Total Points: 325
Total Questions: 120
Total Answers: 131

Location: North Korea
Member since Tue, Jun 16, 2020
4 Years ago
isaacvalentinn questions
Mon, Jan 18, 21, 00:00, 3 Years ago
Mon, Nov 23, 20, 00:00, 4 Years ago
Wed, Sep 23, 20, 00:00, 4 Years ago
;