0

This is how I have my JS set up:

Basically I have a page, and on that page there is a chart. I want to have a loading spinner show while the chart data is loading.

angular.module('myApp', [])
  .service('chartService', ['$http', function($http) {
    var svc = {};
    svc.updateChartData = function($scope) {
      $scope.loading = true;
      $http({method: 'GET', url: 'http://example.com/getjson'})
        .success(function(response) {
          var data = google.visualization.arrayToDataTable(JSON.parse(response));
          var options = {
            ...
        };
      var chart = new google.visualization.ComboChart(document.getElementById('chart_div'));
      chart.draw(data, options);
      $scope.loading = false;
    });
  }

  return svc;
}])

.controller('PageController', ['$scope', '$http', 'chartService', function($scope, $http, chartService) {
  $scope.loading = true;
  // When select option changes
  $scope.updateData = function() {
    chartService.updateChartData($scope);
  };
}])

.controller('ChartController', ['$scope', '$http', 'chartService', function($scope, $http, chartService) {
  // On load
  chartService.updateChartData($scope);
}]);

I am using ng-hide="loading" and `ng-show="loading" to make sure the spinner and the chart show at the correct times.

However, I've noticed that the call below // On load - doesn't actually turn the loading to false. Another message on SO suggested there is a better way to achieve this than by passing $scope around so any suggestions would be appreciated. Thank you.

2
  • 1
    I wouldn't pass the scope to your service, a service is meant to be stateless. Just use the $http callbacks to set your loading to false Commented Oct 16, 2015 at 6:53
  • Aggreed with others keep using callbacks Commented Oct 16, 2015 at 6:57

2 Answers 2

1

It is not a good practice to pass your scope object to a service, a service is meant to be stateless. Instead utilize the callbacks of the $http:

chartService.updateChartData().finally(function(){
    $scope.loading = false;
});

And, as Grundy mentioned below, return your $http from your service to enable callbacks:

svc.updateChartData = function($scope) {
    return $http({ //.. the options });
}

I see some more bad practices though. You shouldn't add the data to your DOM from your service, instead utilize also for this the callbacks:

svc.updateChartData = function($scope) {
    return $http({method: 'GET', url: 'http://example.com/getjson'});
}

controller:

// When select option changes
$scope.updateData = function() {
    chartService.updateChartData().then(function(data) {
        // success
        // do something with the return data from the http call
    }, function (error) {
        // error
        // handle error
    }).finally (function() {
        // always
        $scope.loading = false;
    });
};

For your google chart it would make most sense to create a directive.

Sign up to request clarification or add additional context in comments.

4 Comments

and not forget change svc.updateChartData to return $http result :-) otherwise you get error, because now this function return nothing
Given I use chartService.updateChartData() twice - once in each controller - how can I reduce code repetition? I shouldn't be doing the whole then/finally thing twice?
You could store it in a variable in your service, and get the data from there if it already exists
@b85411 The whole "then/finally thing" is angular's promise system. It is extremely powerful and is at the core of angular itself. Returning the $http call returns a promise so it can be resolved when the ajax call returns so yes it is needed, even if you cache it or not. I do not mean to be rude but your comment sounds like you have little knowledge on the subject, this may help fdietz.github.io/recipes-with-angular-js/… and this docs.angularjs.org/api/ng/service/$q
0

first,you have two controllers,I'm assuming they are nested relations. PageController include ChartController. you want to change the value of the parent controller in the child controller. You must use a reference type rather than a value type.

$scope.loading =true;

change to

$scope.loading ={status:true};

and if you want to set false,Should be

$scope.loading.status =false;

NOT

 $scope.loading ={status:false};

second, you can pass a callback function to service. like this

    svc.updateChartData = function(callback) {
        ....
       .success(){
            callback();
       }
    }  

controller code change to

 .controller('ChartController', ['$scope', '$http', 'chartService', 

function($scope, $http, chartService) {

                // On load
                chartService.updateChartData(function(){
                $scope.loading =true;
                 });

 }]);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.