What's the most concise way to read query parameters in AngularJS?
Asked Answered
K

10

320

I'd like to read the values of URL query parameters using AngularJS. I'm accessing the HTML with the following URL:

http://127.0.0.1:8080/test.html?target=bob

As expected, location.search is "?target=bob". For accessing the value of target, I've found various examples listed on the web, but none of them work in AngularJS 1.0.0rc10. In particular, the following are all undefined:

  • $location.search.target
  • $location.search['target']
  • $location.search()['target']

Anyone know what will work? (I'm using $location as a parameter to my controller)


Update:

I've posted a solution below, but I'm not entirely satisfied with it. The documentation at Developer Guide: Angular Services: Using $location states the following about $location:

When should I use $location?

Any time your application needs to react to a change in the current URL or if you want to change the current URL in the browser.

For my scenario, my page will be opened from an external webpage with a query parameter, so I'm not "reacting to a change in the current URL" per se. So maybe $location isn't the right tool for the job (for the ugly details, see my answer below). I've therefore changed the title of this question from "How to read query parameters in AngularJS using $location?" to "What's the most concise way to read query parameters in AngularJS?". Obviously I could just use javascript and regular expression to parse location.search, but going that low-level for something so basic really offends my programmer sensibilities.

So: is there a better way to use $location than I do in my answer, or is there a concise alternate?

Kienan answered 16/6, 2012 at 13:3 Comment(3)
Shouldn't $location.search()['target'] work?Hawsepipe
This does not work, because there is no hash after the path. http://127.0.0.1:8080/test.html#?target=bob would work, notice the # before the ?. $location is a second level routing method which does not get sent to the server.Laurettelauri
@DanielF @rob, you're both right. $location.search()['target'] works after $locationProvider.html5Mode(true) has been called in a modern browser.Midway
Z
201

You can inject $routeParams (requires ngRoute) into your controller. Here's an example from the docs:

// Given:
// URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
// Route: /Chapter/:chapterId/Section/:sectionId
//
// Then
$routeParams ==> {chapterId:1, sectionId:2, search:'moby'}

EDIT: You can also get and set query parameters with the $location service (available in ng), particularly its search method: $location.search().

$routeParams are less useful after the controller's initial load; $location.search() can be called anytime.

Ziguard answered 16/6, 2012 at 14:57 Comment(6)
Thanks for the suggestion and link! I read the page and also attempted to set up a working sample. However, the approach won't work out for me, because I really don't want to use routing in this case. This was a helpful suggestion nonetheless, and I'll upvote it once I have enough stackoverflow rep.Kienan
I would say @pkozlowski.opensource 's answer is more accurate in this situationSenghor
Not quite .. query paramaters are included in the $routeParams object with the normal route parameters. and you can read them/set them with $location.search(). I'll add that to the answer.Ziguard
Jakub is right. They are different things. When you use the "search" in angular, it appends the query to the hash (end of the URL). The RFC spec for URI states that the query params should prepend (not append) the hash. Thus the "search" is a construct that only angular will extrapolate and interpret. This is why only HTML5 mode will work for the solution above, because you are delegating all requests to one page (at the server level) regardless of the actual URI.Farther
this only works if you want to use route in angularJS !Township
If this isn't working for you, check which router in Angular you are using. In my case I am using $stateProvider which means I would use $stateParams in my controller to get the URL paramsSerum
K
193

Good that you've managed to get it working with the html5 mode but it is also possible to make it work in the hashbang mode.

You could simply use:

$location.search().target

to get access to the 'target' search param.

For the reference, here is the working jsFiddle: http://web.archive.org/web/20130317065234/http://jsfiddle.net/PHnLb/7/

var myApp = angular.module('myApp', []);

function MyCtrl($scope, $location) {

    $scope.location = $location;
    $scope.$watch('location.search()', function() {
        $scope.target = ($location.search()).target;
    }, true);

    $scope.changeTarget = function(name) {
        $location.search('target', name);
    }
}
<div ng-controller="MyCtrl">

    <a href="#!/test/?target=Bob">Bob</a>
    <a href="#!/test/?target=Paul">Paul</a>
    
    <hr/>    
    URL 'target' param getter: {{target}}<br>
    Full url: {{location.absUrl()}}
    <hr/>
    
    <button ng-click="changeTarget('Pawel')">target=Pawel</button>
    
</div>
Kepi answered 16/7, 2012 at 17:56 Comment(12)
do you need the parentheses around $location.search() ?Katelyn
@Magne: yes, you need parentheses, $location.search is a method docs.angularjs.org/api/ng/service/$locationBozovich
Don't forget that if you do not use HTML5Mode(true) then only params after #/ are available or get added to url with #/ at the beginning.Ashaashamed
@nandin: No, you do not need the parentheses around $location.search(). I'm not sure why this answer puts them there.Reider
@nandin, that is a strange argumentFilterable
@Bozovich Parentheses shouldn't be required. The method call return an object and the dot syntax interrogates that object. Akin to a fluent interface.Grimbal
I feel like @Bozovich misunderstood the "Do you need parentheses" question. You do need the parentheses after search, but not the ones around the entirety of $location.search().Unshroud
@K.Carpenter I agree. I edited the answer to fix the syntax. (I know it's an old one, but it's a good reference)Magalimagallanes
Thanks! Yeah, given the large number of upvotes, I figured many people valued this question/answer.Unshroud
@Kepi Currently the JSFiddle link doesn't work. Do you by any chance still have the source code?Auriferous
@UliKöhler you can use ellis-whitehead's code below, should be the sameEellike
A little off topic, but I'm really pleased to see an archive.org link for the jsfiddleHeimer
K
57

To give a partial answer my own question, here is a working sample for HTML5 browsers:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
  <script src="http://code.angularjs.org/1.0.0rc10/angular-1.0.0rc10.js"></script>
  <script>
    angular.module('myApp', [], function($locationProvider) {
      $locationProvider.html5Mode(true);
    });
    function QueryCntl($scope, $location) {
      $scope.target = $location.search()['target'];
    }
  </script>
</head>
<body ng-controller="QueryCntl">

Target: {{target}}<br/>

</body>
</html>

The key was to call $locationProvider.html5Mode(true); as done above. It now works when opening http://127.0.0.1:8080/test.html?target=bob. I'm not happy about the fact that it won't work in older browsers, but I might use this approach anyway.

An alternative that would work with older browsers would be to drop the html5mode(true) call and use the following address with hash+slash instead:

http://127.0.0.1:8080/test.html#/?target=bob

The relevant documentation is at Developer Guide: Angular Services: Using $location (strange that my google search didn't find this...).

Kienan answered 17/6, 2012 at 6:37 Comment(3)
Thanks for this. I've been pulling my hair out trying to read in the query params. Kept getting 'undefined' as you mentioned above. Setting mode to html5 worked.Chadbourne
Shouldn't the ng-controller attribute of <body> be set to MyApp?Lime
Looks like starting with Angular 1.3, in order to use html5Mode you also need to either add a <base href="/" /> tag, or add requireBase: false as another parameter to the call to html5Mode. Details hereEmmeram
H
19

It can be done by two ways:

  1. Using $routeParams

Best and recommended solution is to use $routeParams into your controller. It Requires the ngRoute module to be installed.

   function MyController($scope, $routeParams) {
      // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
      // Route: /Chapter/:chapterId/Section/:sectionId
      // $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
      var search = $routeParams.search;
  }
  1. Using $location.search().

There is a caveat here. It will work only with HTML5 mode. By default, it does not work for the URL which does not have hash(#) in it http://localhost/test?param1=abc&param2=def

You can make it work by adding #/ in the URL. http://localhost/test#/?param1=abc&param2=def

$location.search() to return an object like:

{
  param1: 'abc',
  param2: 'def'
}
Henrieta answered 21/7, 2015 at 18:40 Comment(1)
Thanks for your advice with #/Woorali
C
10

$location.search() will work only with HTML5 mode turned on and only on supporting browser.

This will work always:

$window.location.search

Crone answered 11/7, 2015 at 13:8 Comment(0)
S
6

Just to summerize .

If your app is being loaded from external links then angular wont detect this as a URL change so $loaction.search() would give you an empty object . To solve this you need to set following in your app config(app.js)

.config(['$routeProvider', '$locationProvider', function ($routeProvider,     $locationProvider) 
{
   $routeProvider
      .when('/', {
         templateUrl: 'views/main.html',
         controller: 'MainCtrl'
      })
      .otherwise({
         redirectTo: '/'
      });

      $locationProvider.html5Mode(true);
 }]);
Satori answered 19/4, 2014 at 18:45 Comment(1)
Yep. This was a pain to find. Thanks for the comment.Edveh
M
2

Just a precision to Ellis Whitehead's answer. $locationProvider.html5Mode(true); won't work with new version of angularjs without specifying the base URL for the application with a <base href=""> tag or setting the parameter requireBase to false

From the doc :

If you configure $location to use html5Mode (history.pushState), you need to specify the base URL for the application with a tag or configure $locationProvider to not require a base tag by passing a definition object with requireBase:false to $locationProvider.html5Mode():

$locationProvider.html5Mode({
  enabled: true,
  requireBase: false
});
Mendelssohn answered 11/2, 2016 at 12:44 Comment(0)
M
1

you could also use $location.$$search.yourparameter

Maulstick answered 5/8, 2013 at 11:17 Comment(1)
The $$search is an internal variable, the use of which is not such a good idea.Priority
L
1

I found that for an SPA HTML5Mode causes lots of 404 error problems, and it is not necessary to make $location.search work in this case. In my case I want to capture a URL query string parameter when a user comes to my site, regardless of which "page" they initially link to, AND be able to send them to that page once they log in. So I just capture all that stuff in app.run

$rootScope.$on('$stateChangeStart', function (e, toState, toParams, fromState, fromParams) {
    if (fromState.name === "") {
        e.preventDefault();
        $rootScope.initialPage = toState.name;
        $rootScope.initialParams = toParams;
        return;
    }
    if ($location.search().hasOwnProperty('role')) {
        $rootScope.roleParameter = $location.search()['role'];
    }
    ...
}

then later after login I can say $state.go($rootScope.initialPage, $rootScope.initialParams)

Lame answered 11/1, 2018 at 0:51 Comment(0)
R
-3

It's a bit late, but I think your problem was your URL. If instead of

http://127.0.0.1:8080/test.html?target=bob

you had

http://127.0.0.1:8080/test.html#/?target=bob

I'm pretty sure it would have worked. Angular is really picky about its #/

Rawdin answered 10/3, 2016 at 16:49 Comment(2)
That is only if you are not enabling HTML5 mode. The #/ is not always there in Angular urls.Waterresistant
It is if you are building an SPA. All of my urls have a #. And HTML5Mode means 1) 404 on page refresh and 2) direct urls won't work. Seems like a high price to pay.Lame

© 2022 - 2024 — McMap. All rights reserved.