1

I would like to load 2 json data in $scope inside my controller before doing something like a basic case. But $http inside my factory return the state object because it returns a promise.

My Provider as factory object

(function(angular) {
   'use strict';
    angular.module('myModule').factory("config", function($http) {
        return{
            getConfig: function () {
                return $http.get('json/config.json').then(function(response) {
                    return response.data;
               });
            },
            getPreferences:  function () {
                return $http.get('json/preferences.json').then(function(response) {
                     return response.data;
                });
           }
      }; 
   }); 
})(window.angular);

How to store all my external json data from several files in $scope variables inside my Main Controller without timeout or add several nested promise.then ? Actualy, I would like to know if there is a way to store all json data before the controller loading ?

(function(angular) {
    'use strict';
    angular.module('myModule')
    .controller('MainCtrl', ['$scope', 'config', function ($scope, config, ){
        $scope.config = ?????????? ;
        $scope.preferences = ???????????? ;             
     }]);
})(window.angular);
georgeawg
  • 48,608
  • 13
  • 72
  • 95
J.BizMai
  • 2,621
  • 3
  • 25
  • 49
  • If you use a router such an [ngRoute](https://docs.angularjs.org/api/ngRoute) or [ui-router](https://ui-router.github.io/ng1/docs/latest/), you can use the `resolve` property of the route to wait for several API calls before loading the controller. – georgeawg Jul 19 '17 at 17:02

3 Answers3

3

Just call your methods from your service config in your controller ...

(function(angular) {
    'use strict';
    angular.module('myModule')
    .controller('MainCtrl', ['$scope', 'config', '$q', function ($scope, config, $q) { 
       config.getConfig().then(function(data) {
         $scope.config = data;
       });
       config.getPreferences().then(function(data) {
         $scope.preferences = data;
       });

       // or if you want, 
       // you can wait for the both requests to finish and get them at once
       $q.all([
            config.getConfig(),
            config.getPreferences()
       ])
       .then(function(responses) {
          $scope.config = responses[0];
          $scope.preferences = responses[1];
       });
     }]);
})(window.angular);

In case that you want this configuration data before the initializaation of the controller and you are using router, you can resolve it from the router and it will be available when your controller is triggered.

Router

  $stateProvider
    .state('stateName', {
      url: '/stateURL',
      templateUrl: 'path/to/the/template',
      controller: 'MainCtrl',
      resolve: {
        configData: ['$q', config, function ($q, config) {
          return $q.all([
            config.getConfig(),
            config.getPreferences()
          ]);
       }
     }
   });

Controller

(function(angular) {
  'use strict';
  angular.module('myModule')
    .controller('MainCtrl', ['$scope', 'configData', function ($scope, configData) {
      $scope.config = configData[0];
      $scope.preferences = configData[1];
  }]);

Edited

Yordan Nikolov
  • 2,598
  • 13
  • 16
  • I already did it, but in this case, $scope.config and $scope.preferences are wrapped so scoped. If you do console.log($scope.config) outside the getConfig function, it's wrong because it's 'undefined'. – J.BizMai Jul 19 '17 at 15:34
  • no it is not, it shows that it is `undefined` because it is logged before promise resolve. You can make another test in the controller put this `setTimeout(function(){ console.log($scope, $scope.config); }, 50000); ` and tell what you see ? – Yordan Nikolov Jul 19 '17 at 15:37
  • So, I prefer an other solution that make code with a timeout. I'm pretty sure there is an other way to be certain that data is loaded before the controller and use it directly in it. For me with setTimeout() is a little dirty. I would like to do it properly. – J.BizMai Jul 19 '17 at 15:56
  • I suggested this just like a test case! – Yordan Nikolov Jul 19 '17 at 20:00
2

Simply call your getConfig function and use the returned promise:

config.getConfig()
    .then(function(data) {
        // In your config factory you choose to return reponse.data so in this callback data = response.data
        $scope.config = data
     }

config.getPreferences()
    .then(function(data) {
        // In your config factory you choose to return reponse.data so in this callback data = response.data
        $scope.preferences = data
     }

In your factory you return a promise and configure it to resolve with response.data so this field will directly returned when you resolve the returned promise.

Antoine Amara
  • 645
  • 7
  • 11
  • I already did it, but in this case, $scope.config and $scope.preferences are wrapped so scoped. If you do console.log($scope.config) outside the getConfig function, it's wrong because it's 'undefined'. – J.BizMai Jul 19 '17 at 15:35
  • Indeed, $scope.config will be undefined until the promise is resolved, when the promise is resolved $scope.config will be correct. – Antoine Amara Jul 19 '17 at 15:48
1

My problem is when I have to get two json data, I should do promise.then inside a promise.then. I would like to find a solution to load all json data before execute my code in controller

Use $q.all to wait for several promise to complete:

var configPromise = config.getConfig();
var prefPromise = config.getPreferences();

$q.all([configPromise, prefPromise]).then(function(dataList) {
    $scope.config = dataList[0];
    $scope.preferences = dataList[1];

    console.log($scope.config);
    console.log($scope.preferences);

    //Put more code here
});
        

For more information, see AngularJS $q Service API Reference - $q.all


If you do console.log($scope.config) outside the getConfig function, it's wrong because it's undefined.

That's the way asynchronous APIs work. Code in the success handler executes after all the code on the outside of the .then method.

Put the console.log inside the handler function:

config.getConfig().then(function successHandler(data) {
    $scope.config = data
    //HERE
    console.log($scope.config);
};

//NOT HERE
 ̶c̶o̶n̶s̶o̶l̶e̶.̶l̶o̶g̶(̶$̶s̶c̶o̶p̶e̶.̶c̶o̶n̶f̶i̶g̶)̶;̶

Explaination of Promise-Based Asynchronous Operations

console.log("Part1");
console.log("Part2");
var promise = $http.get(url);
promise.then(function successHandler(response){
    console.log("Part3");
});
console.log("Part4");

pic

The console log for "Part4" doesn't have to wait for the data to come back from the server. It executes immediately after the XHR starts. The console log for "Part3" is inside a success handler function that is held by the $q service and invoked after data has arrived from the server and the XHR completes.

For more information, see How to use $http promise response outside success handler.


Demo

console.log("Part 1");
console.log("Part 2");
var promise = new Promise(r=>r());
promise.then(function() {
    console.log("Part 3");
});
console.log("Part *4*");
Community
  • 1
  • 1
georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • Yes, I saw it. My problem is when I have to get two json data, I should do promise.then inside a promise.then. I would like to find a solution to load all json data before execute my code in controller. – J.BizMai Jul 19 '17 at 16:08
  • Use [$q.all](https://docs.angularjs.org/api/ng/service/$q#all) to wait for several promise. See update to answer. – georgeawg Jul 19 '17 at 16:25