47

My routeProvider for route has reloadOnSearch set to false :

 $routeProvider
     .when(
        "/film/list",
         {
          templateUrl: '/film/list.html', 
          controller: FilmListController,
          reloadOnSearch: false
          } 
          ....

  )

I did this because I don't want the whole controller to be reloaded after query string changes, but I still use it and the url looks like this: film/list?sort=isbn&order=asc&offset=0. Now whenever query string changes I want to call a function from my controller. So I tried to set up a $watch in the controller:

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

But this doesn't work. If I set $watch to watch changes of an individual parameter of a query string, then it works. But I don't want to set $watch for every parameter of my query string. The solution I use now is this:

$scope.location = $location;
$scope.$watch( 'location.url()', function( url ) {
    if($location.path() == '/film/list')
        $scope.list();
 });

So instead of watching for the search, I watch the whole url, and then I check if the path is the one I set in routeProvider to conditionally call the function. What I don't like about this solution is that I have to explicilty type the value for $location.path to be matched.

Can anyone suggest a better solution, and perhaps explain me why is $watch not working for $location.search() ?

MBozic
  • 1,132
  • 1
  • 10
  • 22
  • 2
    Try wrapping location.search() in a function. See http://stackoverflow.com/questions/13465690/changing-the-class-in-angularjs-using-url-data/13468121#13468121 for more info. – Mark Rajcok Feb 26 '13 at 16:46
  • Do a deepwatch. Add true as 3. argument to the watch. – Morten Holmgaard Nov 04 '14 at 14:05
  • possible duplicate of [AngularJS Search Change Event](http://stackoverflow.com/questions/14867772/angularjs-search-change-event) – Léo Lam Feb 19 '15 at 19:36

6 Answers6

109

In case you don't use Angular's route resolution or you just want to know whenever $location changes, there is an event just for that purpose

$rootScope.$on('$locationChangeSuccess', function(event){
        var url = $location.url(),
            params = $location.search();
})
complex857
  • 20,425
  • 6
  • 51
  • 54
Le Duc Duy
  • 1,881
  • 1
  • 19
  • 18
79

You can listen for $routeUpdate event in your controller:

$scope.$on('$routeUpdate', function(){
  $scope.sort = $location.search().sort;
  $scope.order = $location.search().order;
  $scope.offset = $location.search().offset;
});
Stewie
  • 60,366
  • 20
  • 146
  • 113
  • 31
    Just wanted to point out that you HAVE to set reloadOnSearch to false on the route for $routeUpdate to be available. I found this answer via google, and it took me a long time to figure out why $routeUpdate was not available at first. (it is in the docs, I just missed it). – Josh Jun 07 '13 at 17:02
27

Stewies answer is correct, you should listen to $routeUpdate events for that since it's more efficient.

But to answer why your watch isn't working; when you watch location.search(), you're watching if the reference that the search method returns is the same or not. And it will return the reference to the same object every time you call it, which is why your watch isn't firing. That is, even if the search parameters change, it's still the same object that is returned. To get around that, you can pass in true as the third argument to $watch. That will tell Angular to compare by value, not reference. But be careful when creating such watches, because they will consume more memory, and take longer to execute. So that's why you should do it like stewie said.

d-_-b
  • 21,536
  • 40
  • 150
  • 256
Anders Ekdahl
  • 22,685
  • 4
  • 70
  • 59
  • Great, this is really good explanation! I will accept stewie's answer then if you don't mind since he posted the answer earlier, although this explanation makes the answer complete. – MBozic Feb 26 '13 at 20:53
  • the suggestion to use the 3rd argument to $watch (objectEquality) is a great tip. See http://stackoverflow.com/questions/14867772/angularjs-search-change-event/14868586#14868586 as well – Mike Ohlsen Apr 23 '14 at 14:22
  • Great explanation. – Javeed May 03 '19 at 08:02
12

Use

$scope.$watch(function(){ return $location.search() }, function(params){
    console.log(params);
});

instead.

Thomas Decaux
  • 21,738
  • 2
  • 113
  • 124
1

Watch the change on routeUpdate like this in controller:

 $scope.$watch('$routeUpdate', function(){
     $scope.sort = $location.search().sort;
     $scope.order = $location.search().order; 
 });
Maen
  • 10,603
  • 3
  • 45
  • 71
CodeOverRide
  • 4,431
  • 43
  • 36
1

$watch(watchExpression, listener, [objectEquality]);

watchExpression => should be either

  1. A string evaluated as expression or
  2. A function called with current scope as a parameter

So here, You should pass your first parameter as a function.

JavaScript Example =>

$scope.$watch (
                function () { return $scope.$location.search(); };
                function (value) { console.log(value); } 
              );

Typescript example =>

this.$scope.$watch (
                     () => { return this.$location.search(); },
                     (value: any) => { console.log(value); }
                   ) ;

This did work for me.

Dilushika Weerawardhana
  • 1,441
  • 1
  • 12
  • 16