Pass URL to as $routeParam in AngularJS app

29,982

Solution 1

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.

Solution 2

Using $routeProvider in Angular 1.2, you can pass in a url if it's at the end of the path by adding an asterik to the pattern. The following should work whether or not you URLComponentEncode the url.

The route:

angular.module('angularApp', ['ngRoute'])
      .when('/frame/:picture_url*', {
        templateUrl: 'views/frame.html',
        controller: 'PictureFrame'
      });

The controller:

      .controller('PictureFrame', function($scope, $routeParams, $sce){
        //whitelist the URL
        $scope.picture_url = $sce.trustAsResourceUrl($routeParams.picture_url);
      });

Then in your template:

<iframe ng-src="{{picture_url}}"></iframe>

Solution 3

I have a solution but I don't know if it will help you. From Angular documention http://docs.angularjs.org/api/ng.$location $location has a function search(search, paramValue)

To pass the parameter:

parameter = encodeURIComponent url
$location.search({ yourURLParameter: parameter }).path('/preview')

To read the parameter:

url = decodeURIComponent $location.search().yourURLParameter

Of course you need to inject $location dependency

Share:
29,982
Rafal Pastuszak
Author by

Rafal Pastuszak

Developer · Designer · Sometimes speaks Persian in sleep.

Updated on July 22, 2020

Comments

  • Rafal Pastuszak
    Rafal Pastuszak almost 4 years

    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.