15

I would like to delay the initialization of a controller until the necessary data has arrived from the server.

I found this solution for Angular 1.0.1: Delaying AngularJS route change until model loaded to prevent flicker, but couldn't get it working with Angular 1.1.0

Template

<script type="text/ng-template" id="/editor-tpl.html">
Editor Template {{datasets}}
</script>
    <div ng-view>
</div>

JavaScript

function MyCtrl($scope) {    
    $scope.datasets = "initial value";
}

MyCtrl.resolve = {
    datasets : function($q, $http, $location) {
        var deferred = $q.defer();

        //use setTimeout instead of $http.get to simulate waiting for reply from server
        setTimeout(function(){
            console.log("whatever");
            deferred.resolve("updated value");
        }, 2000);

        return deferred.promise;
    }
};

var myApp = angular.module('myApp', [], function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: '/editor-tpl.html',
        controller: MyCtrl,
        resolve: MyCtrl.resolve
    });
});​

http://jsfiddle.net/dTJ9N/1/

Community
  • 1
  • 1
mb21
  • 34,845
  • 8
  • 116
  • 142

3 Answers3

11

Since $http returns a promise, it's a performance hit to create your own deferred just to return the promise when the http data arrives. You should be able to do:

MyCtrl.resolve = {
    datasets: function ($http) {
        return $http({method: 'GET', url: '/someUrl'});
    }
};

If you need to do some processing of the result, use .then, and your promise is chained in for free:

MyCtrl.resolve = {
    datasets: function ($http) {
        return $http({method: 'GET', url: '/someUrl'})
               .then (function (data) {
                   return frob (data);
               });
    }
};
Michael Robinson
  • 29,278
  • 12
  • 104
  • 130
mdorman
  • 356
  • 1
  • 6
  • 10
    I'd say the performance hit for wrapping $http's promise with another promise in this case is so small it's not worth worrying about. However, the more concise code resulting from the extra promise's exclusion is a worthy pursuit. – Tim Harper Nov 06 '12 at 16:21
  • Code is nice and clean, but as of today, the fiddle is broken, shows document source. Too new to Angular to be much help beyond noting that. – enigment Dec 16 '12 at 13:09
  • the fiddle is not broken, it displays the result of the http request :) – mb21 Feb 15 '13 at 15:45
4

You could always just put "ng-show" on the outer-most DOM element and set it equal to the data you want to wait for.

For the example listed on the Angular JS home page you can see how easy it is: http://plnkr.co/CQu8QB94Ra687IK6KgHn All that had to be done was That way the form won't show until that value has been set.

Much more intuitive and less work this way.

stormlifter
  • 3,781
  • 3
  • 17
  • 20
  • what if you have multiple data loads? you don't know which one will finish as they all run asyn. For instance...assume I need the 5 options to show in a select box and all the data relative to the first option, so I can preload the form??? – Jason May 13 '13 at 21:48
  • If you got five, no problem, after each one loads just make note and once all five are done be happy. You only need to have some variable with an array and check that the length is 5 after each data load add to it and after 5 set another variable that ng-show is based off and reset your counting array. – stormlifter Oct 06 '15 at 14:58
1

You can take a look at a near identical question here that uses resources, but it works the same way with $http. I think this should work

function MyCtrl($scope, datasets) {    
    $scope.datasets = datasets;
}

MyCtrl.resolve = {
    datasets: function($http, $q) {
        var deferred = $q.defer();

        $http({method: 'GET', url: '/someUrl'})
            .success(function(data) {
                deferred.resolve(data)
        }

        return deferred.promise;
    }
};

var myApp = angular.module('myApp', [], function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: '/editor-tpl.html',
        controller: MyCtrl,
        resolve: MyCtrl.resolve
    });
});​
Community
  • 1
  • 1
Justen
  • 4,859
  • 9
  • 44
  • 68
  • Thanks, fixed a syntax error or two, here the working code: http://jsfiddle.net/dTJ9N/2/ – mb21 Sep 10 '12 at 23:17