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?
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.
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">
Bob
Paul
<hr/>
URL 'target' param getter: {{target}}<br>
Full url: {{location.absUrl()}}
<hr/>
<button ng-click="changeTarget('Pawel')">target=Pawel</button>
</div>
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...).
It can be done by two ways:
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;
}
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¶m2=def
You can make it work by adding #/ in the URL. http://localhost/test#/?param1=abc¶m2=def
$location.search() to return an object like:
{
param1: 'abc',
param2: 'def'
}
$location.search() will work only with HTML5 mode turned on and only on supporting browser.
This will work always:
$window.location.search
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);
}]);
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
});
you could also use $location.$$search.yourparameter
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)
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 #/
Related
This is my URL :
http://localhost:8091/apps/dashboard/#!/#%2Findex%3Fsession=564badc5919b42fa880f1b34ae5d0740
I am getting 'undefined' from the below mentioned code:
$location.search().session
What is wrong here ? I am new with Angular and wanted to get session variable from the query string.
Thanks in advance !
It is required to include $locationProvider in your app.config.
Then, you need to configure html5Mode before using $location.search().
Below sample will help you.
app.config(['$routeProvider', '$locationProvider', function($routeProvider,
$locationProvider) {
$routeProvider
.when("/", {
templateUrl : 'xxx.html'
, controller: 'xxxController'
});
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
Cheers!
Figured the construction of my URL was incorrect. Re-constructed the URL and all started working as expected.
For me , this was the correct URL format :
http://localhost:8091/apps/SPMDashboardApp/#!/index?session==564badc5919b42fa880f1b34ae5d0740
You can try this one as well:
Since AngularJS provides the query string as a set of key/value pairs,
use something like this:
URL - http://my.site.com/?myparam=33
if ( $location.search().hasOwnProperty( 'myparam' ) ) {
var myvalue = $location.search()['myparam'];
// 'myvalue' now stores '33'
}
You must inject $location into your Controller.
It’s easy to forget because there are two locations (like all injections):
app.controller( 'MyController', [ '$scope', '$location', function( $scope, $location )
You must add a ‘base’ element to your HTML. (very important when your application goes to production)
This line is necessary to support $location HTML5 mode (for query
string parameters).
<base href="/">
I posted a question recently about how to set parameters in the URL with Angularjs so that they could be preserved on page reload. But it caused a problem with Google Maps.
I am using ngRoute to navigate around my application. And the problem that I've experienced with setting parameters in the URL, was that every time I would set a parameter (be it $location.search() or just a plain old window.location.hash='something'), the Google Maps map would get unloaded. I tried changing parameter names, because I thought Google Maps listens to some of those options by default. But that wasn't the case.
Once I got rid of the ngRoute code completely, and instead of the ngView directive, I included my pages with ng-include, the map didn't get unloaded anymore when I manipulated the parameters.
I'm not that good as to know exactly what or why is going on, but I would guess that ngRoute thinks it has to compile my template file again because "something" changed in the URL. So what I would like, is to explain to ngRoute somehow, that if the part after ? changed, then it shouldn't try to compile my template file again (and subsequently destroy the loaded Google Maps), because those are just my additional options. But if the part before ? changed, then that's fine, because then the page changed.
Or, is there another, better, more Angular-way of getting around this issue?
This is my ngRoute configuration:
app.config(function($httpProvider, $routeProvider) {
// Routing
$routeProvider.when("/", {
redirectTo: "/Map"
}).when("/Map", {
controller: "MapController",
templateUrl: "tpl/view/map.html"
}).when("/Table", {
controller: "TableController",
templateUrl: "tpl/view/items-table.html"
}).otherwise({
templateUrl: "tpl/view/404.html"
});
});
This is my code for changing pages:
$scope.navigate = function(location) {
$location.path(location);
};
And this is how I would set up a custom GET parameter, as per the code from my other Stackoverflow question:
var params = $location.search();
params.source = source.filename;
$location.search(params);
You're looking for the reloadOnSearch property.
app.config(function($httpProvider, $routeProvider) {
...
}).when("/Map", {
controller: "MapController",
templateUrl: "tpl/view/map.html",
reloadOnSearch: false
})
...
});
https://docs.angularjs.org/api/ngRoute/provider/$routeProvider
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?
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.
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">
Bob
Paul
<hr/>
URL 'target' param getter: {{target}}<br>
Full url: {{location.absUrl()}}
<hr/>
<button ng-click="changeTarget('Pawel')">target=Pawel</button>
</div>
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...).
It can be done by two ways:
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;
}
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¶m2=def
You can make it work by adding #/ in the URL. http://localhost/test#/?param1=abc¶m2=def
$location.search() to return an object like:
{
param1: 'abc',
param2: 'def'
}
$location.search() will work only with HTML5 mode turned on and only on supporting browser.
This will work always:
$window.location.search
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);
}]);
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
});
you could also use $location.$$search.yourparameter
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)
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 #/
I'm working on a Django app which makes heavy use of Angular in some pages, e.g. at domain.com/myAngularApp
Within the angular page I'm using Angular routing for navigating between different views/states within that page. However across the whole website there are navigation links which need to result in round trip requests to Django. However all the pages include the same compiled javascript file which includes the Angular route declarations.
So my question is: how to I get Angular to mange its own routes and get out of the way when the location is changed (primarily by clicking a link on the page) to a path that it hasn't explicitly been told to own, i.e. to different subdirectories off the domain.
My routing declaration looks something like:
myApp.config( function($locationProvider, $routeProvider) {
$locationProvider.html5Mode(true);
$routeProvider.when('/myAngularApp/', {
templateURL: 'template1.html'
});
$routeProvider.when('/myAngularApp/stuff', {
templateURL: 'template12.html'
});
$routeProvider.otherwise({redirectTo: <not sure what to do here...> });
})
I've tried something like:
$routeProvider.otherwise({redirectTo: function(a1,r,a3){ window.location.href = r }})
But this makes the page refresh endlessly on any non-matched route.
Leaving out the otherwise statement seems to make it impossible to leave a page with a non-matched route when accessed directly... don't really understand why?
It must be possible to do what I want no?
I think I may have found a solution. I'm doing a similar thing, a multi-page angular site that uses angular for some of it's pages. Here's what I'm doing
var app = angular.module('appname', ['ui.bootstrap', 'ui.autocomplete'])
.config(['$locationProvider', '$routeProvider', function($locationProvider, $routeProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('!');
}])
.run(function($rootScope, $location) {
var redirected = false;
$rootScope.$on('$locationChangeStart', function(event, nextLocation, currentLocation) {
if(!redirected && $location.path() !== '/current-url') {
redirected = true;
event.preventDefault();
window.location = $location.path();
}
});
});
So what I have to work out next is how to pass in the current-url path. One way I'm thinking is to us ng-init to set that data in the view (I'm using express.js so I'd use Jade). Or possibly in the run function grab the initial path and test against that.
The event.preventDefault() is there to stop an extra item being added to the browsers history. Before I did that I had to hit back twice to get back to the angular page.
Note This hasn't been tested with IE8 yet. I'm going to do that now and see how it fairs.
Update Ok I just tested this in IE8 and I got stuck in a redirect loop. I've updated the code to have a simple variable to check if we've redirected. Seems to work. I'd love to know a prettier way.
I am implementing authentication checking using $routeChangeStart as explained here and looking for a way to preserve all detail provided in the next object. AngularJS documentation on $route shows that you can set the $route.current and I was hoping that I do something like the following to change the $route instead of using $location:
$route.current = { templateUrl: 'detail.html', controller: 'MainCtrl' };
$route.reload();
I know that $route.current can be updated because console.log after setting it shows that it does pickup the change:
console.log("current route: ", $route.current.templateUrl );
Unfortunately it does not work. The application I am creating have additional parameters defined using $routeProvider that I would like to preserve.
Any ideas?
Here is a Plunker I setup to illustrate this.