Angular factory returning a promise

后端 未结 4 566
我在风中等你
我在风中等你 2020-12-08 09:40

When my app starts I load some settings from a server. Most of my controllers need this before anything useful can be done. I want to simplify the controller\'s code as much

4条回答
  •  [愿得一人]
    2020-12-08 10:20

    Somewhere you would need to "wait".

    The only built-in way in Angular to completely absolve the controller from having to wait on its own for async data to be loaded is to instantiate a controller with $routeProvider's route's resolve property (or the alternative $stateProvider of ui.router). This will run controller only when all the promises are resolved, and the resolved data would be injected.

    So, ng-route alternative - plunker:

    $routeProvider.when("/", {
       controller: "SomeCtrl",
       templateUrl: "someTemplate.html",
       resolve: {
         settings: function(settingsSvc){
           return settingsSvc.load(); // I renamed the loading function for clarity
         }
       });
    

    Then, in SomeCtrl you can add settings as an injectable dependency:

    .controller("SomeCtrl", function($scope, settings){
       if (settings.foo) $scope.bar = "foo is on";
    })
    

    This will "wait" to load someTemplate in

    until settings is resolved.

    The settingsSvc should cache the promise so that it won't need to redo the HTTP request. Note, that as mentioned in another answer, there is no need for $q.defer when the API you are using (like $http) already returns a promise:

    .factory("settingsSvc", function($http){
       var svc = {settings: {}};
       var promise = $http.get('/api/public/settings/get').success(function(data){
          svc.settings = data; // optionally set the settings data here
       });
       svc.load = function(){
          return promise;
       }
       return svc;
    });
    

    Another approach, if you don't like the ngRoute way, could be to have the settings service broadcast on $rootScope an event when settings were loaded, and controllers could react to it and do whatever. But that seems "heavier" than .then.

    I guess the third way - plunker - would be to have an app-level controller "enabling" the rest of the app only when all the dependencies have preloaded:

    .controller("AppCtrl", function($q, settingsSvc, someOtherService){
       $scope.loaded = false;
       $q.all([settingsSvc.load, someOtherService.prefetch]).then(function(){
          $scope.loaded = true;
       });
    });
    

    And in the View, toggle ng-if with loaded:

    
      
    loading app...

提交回复
热议问题