jonhobbs
jonhobbs

Reputation: 27972

Angular-UI - Transitioning to same state updates URL but back button broken

I have the following code in my Ui-router setup

.state('contacts.list', {
    url: "",
    templateUrl: "templates/contactsList.tpl.html",
    controller: "ContactsListCtrl"  contactsFilter: "all"
    }
})
.state('contacts.list.paginated', {
    url: "/page/{pageNumber}",
    templateUrl: "templates/contactsList.tpl.html",
    controller: "ContactsListCtrl"
})

Then I have an Angular-ui pagination control which calls this function when the page is changed

$scope.pageChanged = function(pageNumber){

    $state.go("contacts.list.paginated", {pageNumber: pageNumber});

    $scope.listParams.offset = (pageNumber -1) * $scope.itemsPerPage;

    updateList(); // Reloads the data for the new page from the server */

};

This kind of works. When you click from page 1 to page 2 it gets the next page of data from the server, puts it on the page and changes the URL in the address bar to /2. Also, presumably because the two states share the same controller, the controller is not re-executed, this is what I want because I want it to continue executing my $scope.pageChanged() function, which it does.

So far so good. URL updating, data updating.

The problem is that when I click the back button, nothing happens. If hit page 2, 3, then 4 and then hit the back button the URL changes back to /page3 as expected, but the controller code is not executed and the pagination control is not updated and no call is made to the sever to get the correct data.

Also, if you are already in this view, manually typing /page/2 or /page/3 into the address bar causes no code to be executed, presumably because it's just a change of params.

I need to find a way of either getting it to re-execute the controller code when you use the back/forward button, or listening out for the change in state/location so that I can refresh the data when this happens (but not run it twice when you hit the page for the first time).

I have scoured SO and the rest of the web but cannot find any clues.

Upvotes: 2

Views: 1320

Answers (2)

jonhobbs
jonhobbs

Reputation: 27972

OK, I think I have a solution.

The solution to getting the back button to work was to do this...

$scope.$watchCollection('$stateParams', function(){

    ... calculate page number and offet here...

    updateList();

});

This triggers every time you use $state.go() AND every time you use the browser back and forward button, as opposed to putting it in your controller code which is not re-executed when you hit the back button.

The problem I had initially with this approach was that sometimes I was updating my results separately and the state was changing so my watch was getting triggered and my AJAX code was being called twice. The key was to never update the list of results outside of this watch and make sure every time you want them to update you change the state or the state params first.

Upvotes: 1

Bradley Trager
Bradley Trager

Reputation: 3590

Try injecting the $rootScope into your controller and listening for the $stateChangeSuccess even like so:

$rootScope.$on('$stateChangeSuccess', function() {
    $scope.listParams.offset = (pageNumber -1) * $scope.itemsPerPage;
    updateList(); // Reloads the data for the new page from the server */
});

Upvotes: 1

Related Questions