0

There's no problem with populating a service (factory actually) with asynchronous data. However, what is the proper way of updating data in a service?

The problem that I run into is that all async data is access with .then() method, basically a promise resolve. Now, how would I put something into a service, and update related views?

The service I'm using:

function ($q) {
    var _data = null;
    return {
        query: function (expire) {
            var defer = $q.defer();
            if (_data) {
                defer.resolve(response.data);
            } else {
                $http.get('/path').then(function (response) {
                    defer.resolve(response.data);
                });
            }
            return defer.promise;
        }
        ,
        byId: function(id) {
            var defer = $q.defer();
            this.query().then(function(data){
                angular.forEach(data, function(item) {
                    if (item.id == id) {
                        return defer.resolve(item);
                    }
                });
                return defer.reject('id not found');
            });
            return defer.promise;
        }
        ,
        add: function(item) {
            ...
        }
    };
}

What would be good implementation of add method? Note, that I'm working with Angular >1.2

1 Answer 1

1

I've posted a few examples to show ways to get data from your service into your controllers and thereby allow the data to be bound in the views.

http://plnkr.co/edit/ABQsAxz1bNi34ehmPRsF?p=preview

The HTML

<!DOCTYPE html> <html>

  <head>
    <script data-require="angular.js@*" data-semver="1.2.4" src="http://code.angularjs.org/1.2.3/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>   </head>

  <body ng-app="myApp">
    <div ng-controller="MyCtrl">
      {{sharedData.label}}
      <br>
      <input type="text"  ng-model="sharedData.label"/>
    </div>
    <div ng-controller="MyCtrl2">
      <input type="text"  ng-model="sharedData.label"/>
      <button ng-click="updateValue()">test</button>
    </div>
    <div ng-controller="MyCtrl3">
      <input type="text"  ng-model="sharedData.label"/>
      <button ng-click="updateValue()">test</button>
    </div>
    <div ng-controller="MyCtrl4">
      <input type="text"  ng-model="sharedData.label"/>
    </div>
       </body>

</html>

The JS

angular.module("myApp", []).service("MyService", function($q) {
  var serviceDef = {};
  //It's important that you use an object or an array here a string or other
  //primitive type can't be updated with angular.copy and changes to those
  //primitives can't be watched.
  serviceDef.someServiceData = {
    label: 'aValue'
  };
  serviceDef.doSomething = function() {
    var deferred = $q.defer();

    angular.copy({
      label: 'an updated value'
    }, serviceDef.someServiceData);

    deferred.resolve(serviceDef.someServiceData);
    return deferred.promise;
  }
  return serviceDef;
}).controller("MyCtrl", function($scope, MyService) {
  //Using a data object from the service that has it's properties updated async
  $scope.sharedData = MyService.someServiceData;
}).controller("MyCtrl2", function($scope, MyService) {
  //Same as above just has a function to modify the value as well
  $scope.sharedData = MyService.someServiceData;
  $scope.updateValue = function() {
    MyService.doSomething();
  }
}).controller("MyCtrl3", function($scope, MyService) {
  //Shows using a watch to see if the service data has changed during a digest
  //if so updates the local scope
  $scope.$watch(function(){ return MyService.someServiceData }, function(newVal){
    $scope.sharedData = newVal;
  })
  $scope.updateValue = function() {
    MyService.doSomething();
  }
}).controller("MyCtrl4", function($scope, MyService) {
  //This option relies on the promise returned from the service to update the local
  //scope, also since the properties of the object are being updated not the object
  //itself this still stays "in sync" with the other controllers and service since
  //really they are all referring to the same object.
  MyService.doSomething().then(function(newVal) {
    $scope.sharedData = newVal;
  });
});

Regarding the add method in the service you'd want it to do something similar to a get, just create a deferred that you return the promise from and then do your async business (http request). For your byId function you may want to use a cached version (save the data that comes back from the query call in a property of the service). This way the query doesn't need to be executed every time if that's not necessary.

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

1 Comment

Actually, I already am caching the backend calls in real life scenario. :) Then, deferrals for everything. Will give it a go. However, it is the first idea I had myself, just it seemed "wrong".

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.