In Angular 1.6, how to get properly config data from several json using in $scope inside controller?

547 Views Asked by At

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);
3

There are 3 best solutions below

4
On BEST ANSWER

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

2
On

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.

2
On

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*");