Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS update view with service/model changes using $q promises

I'm trying to load data from a service and update the view using $q, but it's not working. It works if I put the http call inside the controller, but I'd prefer it be part of the service.

Any help? Also, is there a better way to do this instead of promises?

Demo and code below.

---------- Fiddle Demo Link ----------

View

<div ng-init="getData()">
  <div ng-repeat="item in list">{{item.name}}</div>
</div>

Controller

.controller('ctrl', ['$scope', 'dataservice', '$q', function ($scope, dataservice, $q) {

  $scope.list = dataservice.datalist;

  var loadData = function () {
    dataservice.fakeHttpGetData();
  };

  var setDataToScope = function () {
    $scope.list = dataservice.datalist;
  };

  $scope.getData = function () {
    var defer = $q.defer();
    defer.promise.then(setDataToScope());
    defer.resolve(loadData());
  };

}])

Service

.factory('dataservice', ['$timeout', function ($timeout) {

  // view displays this list at load 
  this.datalist = [{'name': 'alpha'}, {'name': 'bravo'}];

  this.fakeHttpGetData = function () {
    $timeout(function () {

      // view should display this list after 2 seconds
      this.datalist = [{'name': 'charlie'}, {'name': 'delta'}, {'name': 'echo'}];
    },
    2000);
  };

  return this;
}]);
like image 370
ozandlb Avatar asked Aug 20 '26 15:08

ozandlb


2 Answers

No need for ngInit or $q. This is how you should do it.

You should also not expose dataservice.list to the controller. That should be private to dataservice, which will contain most of the logic to determine whether to send the controller the existing list or update the list and then send it.

angular.module('app', [])

        .controller('ctrl', ['$scope', 'dataservice', function ($scope, dataservice) {

            loadData();

            function loadData() {
                dataservice.fakeHttpGetData().then(function (result) {
                    $scope.list = result;
                });
            }
        }])

        .factory('dataservice', ['$timeout', function ($timeout) {

            var datalist = [
                {
                    'name': 'alpha'
                },
                {
                    'name': 'bravo'
                }
            ];

            this.fakeHttpGetData = function () {

                return $timeout(function () {

                            // Logic here to determine what the list should be (what combination of new data and the existing list).

                            datalist =  [
                                {
                                    'name': 'charlie'
                                },
                                {
                                    'name': 'delta'
                                },
                                {
                                    'name': 'echo'
                                }
                            ];

                            return datalist;
                        },
                        2000);
            };

            return this;
        }]);
like image 71
dave walker Avatar answered Aug 23 '26 06:08

dave walker


Firstly, don't use ng-init in this way. As per the docs:

The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.

Secondly, promises are the perfect thing to use in this case, but you don't need to touch $q, as $http calls return promises for you.

To do this properly, simply return the $http result from the service:

this.getDataFromService = function() {
    return $http(/* http call info */);
};

Then, inside you controller:

dataservice.getDataFromService().then(function(result){
    $scope.list = result.data;
});    

Also here is the updated fiddle: http://jsfiddle.net/RgwLR/

Bear in mind that $q.when() simply wraps the given value in a promise (mimicking the response from $http in your example).

like image 36
Matt Way Avatar answered Aug 23 '26 07:08

Matt Way