stilllife stilllife - 1 month ago 11
AngularJS Question

Angular view not updating when refreshing data coming from service with $http

This might be a question asked 10 thousand times but I still can't find a proper solution to this problem.

I just have the classical example of controller getting the data from a service, which in turn uses

$http
to fetch some data asynchronously from an API (or JSON file). Because the data in the controller are a reference to an object in the service, I was expecting the data to change when the service has done its job, and so I was expecting the view to update. As you can see in the snippet below the view retains the initial value.

Thus, my two questions:


  1. Is this a good pattern? Imagine that the controllers are more than one and that at certain point the data needs to be re-fetch (and so updated in any controller)

  2. Why the view is not updating ?



Keep in mind that I don't want to use $scope (if possible) nor $rootSscope, nor $watch.



var app = angular.module('myApp', [])
.service('Service', ['$http', service])
.controller('Controller', ['Service', controller]);


function controller(Service){
var c = this;
c.items = [
{'title': 'Default title1'},
{'title': 'Default title2'}
];
c.items = Service.data;
}

function service($http, $scope){
var data = [
{'title': 'olddatatitle'}
];
$http({
method: 'GET',
url: 'https://jsonplaceholder.typicode.com/posts?userId=1',
cache: true
}).then(function(response){
data = response.data;
});
return {
data: data
}
}

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" >
<ul ng-controller="Controller as c">
<li ng-repeat="item in c.items">
{{item.title}}
</li>
</ul>
</div>




Answer

Your problem is an asynchronous one... You don't want to get your data from $http in your service and expect it to be ready when your controller is ready for it. Don't try to return data, instead return a promise of data:

function service($http, $scope){
    //var data = [
    //  {'title': 'olddatatitle'}
    //];

    return {
      getData: function() {
        return $http({    // calls to $http return a promise object
          method: 'GET',
          url: 'https://jsonplaceholder.typicode.com/posts?userId=1',
          cache: true
        });
    }
}

That way your controller will know exactly when the data is ready to be consumed:

function controller(Service){
    var c = this;
    //c.items = [
    //  {'title': 'Default title1'},
    //  {'title': 'Default title2'}
    //];
    Service.getData().then(function(response) {    // the promise object allows you to assign data only when it's ready
      c.items = response.data
    });
}