0

My problem is, on page load, the data received from AJAX may not be ready when the $routeChangeSuccess event fires. In other words, I have a race condition between the event handler and AJAX.

How do I ensure this data is there when the event fires on page load.

Here is a fiddle demonstrating the problem: http://jsfiddle.net/ajrt4/3/

My scenario: I have a select box which is populated by data received by an $http AJAX call. This select box is used for navigation represents a set of routes the user can select and then go to. Because the select box needs to have the selected option reflect their current route at all times, I use an event handler on the $routeChangeSuccess event to change the selected drop down when the user uses the back and forward buttons in their browser.

Here is an example of what I have in my controller:

$http.get('getData').success(function(data) {
    // Sets the dropdown menu data
    $scope.data = data;
});

$scope.$on("$routeChangeSuccess", function(event, currentRoute){
    // Set the selected option here based off the route
    if (typeof $scope.selected == 'undefined') {
        for (var i = 0; i < $scope.data.length; i++) {
            if ($scope.data[i].name == $routeParams.name) {
                $scope.selected = $scope.data[i]; // sets the current selection based off the route parameter
                return;
            }
        }
    }
});

View:

<select ng-model="selected" ng-options="option as option.name for option in data"></select>

The data received needs to be only loaded once so I do not need or want to use resolve on the $routeProvider since this used with another controller and view anyway. This also means I will not nest the AJAX call in the event handler and redundantly get the data. What I think might solve this is using the Angular's $q promise API, but not sure how.

Dean Or
  • 2,822
  • 2
  • 26
  • 25
  • I'm not sure to completely understand the question, but why don't you simply hide the select box until the data and the current route are available? – JB Nizet Jun 11 '14 at 21:04
  • I edited to make the problem more clear. It's not a problem with the view so much that $scope.data is not guarenteed to be set to use in the event handler. – Dean Or Jun 11 '14 at 22:44

1 Answers1

0

Although, it might be not the Angular or event the most correct way to do it but what I normally do in these scenarios is putting the event handler registration code into the .then block. In your case given the $http service returns a promise you could do the following

$http.get('getData').then(function(data) {
    // Sets the dropdown menu data
    $scope.data = data;
    $scope.$on("$routeChangeSuccess", function(event, currentRoute){
    ...
    }
});

But then again... you are using success which is effectively the same. The question is why are you not putting the handler registration inside your success block.

  • This just sets the event handler at a later point. The event itself can still occur before the GET request finishes. – Dean Or Jun 25 '14 at 17:46
  • I see. Then I would cache the $routeChangeSuccess events in an array. As soon as you're ready from loading the http data, in your success callback or in .then block you can pop the latest from your array and process it. In your $routeChangeSuccess handler you will need to check if you need to cache the event or process it straight away (by maybe having a variable that is set to false initially and then set to true when you have loaded the data and processed the last cache request) –  Jun 27 '14 at 09:42