Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular UI Router Reload Controller on Back Button Press

I have a route that can have numerous optional query parameters:

$stateProvider.state("directory.search", {
                url: '/directory/search?name&email',
                templateUrl: 'view.html',
                controller: 'controller'

When the user fills the form to search the directory a function in the $scope changes the state causing the controller to reload:

 $scope.searchDirectory = function () {
            $state.go('directory.search', {
                name: $scope.Model.Query.name,
                email: $scope.Model.Query.email
            }, { reload: true });                   
        };

In the controller I have a conditional: if($state.params){return data} dictating whether or not my service will be queried.

This works great except if the user clicks the brower's forward and/or back buttons. In both these cases the state (route) changes the query parameters correctly but does not reload the controller.

From what I've read the controller will be reloaded only if the actual route changes. Is there anyway to make this example work only using query parameters or must I use a changing route?

like image 330
miniscem Avatar asked May 05 '15 21:05

miniscem


2 Answers

You should listen to the event for succesful page changes, $locationChangeSuccess. Checkout the docs for it https://docs.angularjs.org/api/ng/service/$location.

There is also a similar question answered on so here How to detect browser back button click event using angular?.

When that event fires you could put whatever logic you run on pageload that you need to run when the controller initializes.

Something like:

$rootScope.$on('$locationChangeSuccess', function() {
    $scope.searchDirectory()
});  

Or better setup like:

var searchDirectory = function () {
    $state.go('directory.search', {
        name: $scope.Model.Query.name,
        email: $scope.Model.Query.email
    }, { reload: true });

$scope.searchDirectory = searchDirectory;

$rootScope.$on('$locationChangeSuccess', function() {
    searchDirectory();
});  
like image 160
Andrew Clavin Avatar answered Nov 06 '22 01:11

Andrew Clavin


Using the above, I was able to come up with a solution to my issue:

controller (code snippet):

...var searchDirectory = function (searchParams) {

            if (searchParams) {
                $scope.Model.Query.name = searchParams.name;
                $scope.Model.Query.email = searchParams.email;
            }

            $state.go('directory.search', {
                name: $scope.Model.Query.name,
                email: $scope.Model.Query.email,
            }, { reload: true });                   
        };...

       $rootScope.$on('$locationChangeSuccess', function () {
            //used $location.absUrl() to keep track of query string
            //could have used $location.path() if just interested in the portion of the route before query string params
            $rootScope.actualLocation = $location.absUrl(); 
        });

        $rootScope.$watch(function () { return $location.absUrl(); }, function (newLocation, oldLocation) {
            //event fires too often? 
            //before complex conditional was used the state was being changed too many times causing a saturation of my service
            if ($rootScope.actualLocation && $rootScope.actualLocation !== oldLocation && oldLocation !== newLocation) {
                searchDirectory($location.search());
            }
        });

        $scope.searchDirectory = searchDirectory;

 if ($state.params && Object.keys($state.params).length !== 0)
{..call to service getting data...}

This solution feels more like a traditional framework such as .net web forms where the dev has to perform certain actions based on the state of the page. I think it's worth the compromise of having readable query params in the URL.

like image 34
miniscem Avatar answered Nov 05 '22 23:11

miniscem