1

I'm new to angularjs. In my webapp I'm trying to work with user contacts as follows.

SERVICE

 app.service('Contacts', function ($http,$timeout,$q) {
    return {
        getData: function() {
            var defer = $q.defer();
            $http.get('../ListContacts')
            .success(function(data) {
                defer.resolve(data);
            });
            return defer.promise;
        }
    }
 });

ContactsController, OtherControllers

$scope.contactsBook = {};
...
Contacts.getData().then(function(data) {
    $scope.contactsBook = data;
});

I found the above method somewhere in SO itself. I used it because I don't want to use separate module for Contacts.

I can get data at page load. I can update my contacts at server through ajax posts (from ContactsController). Now I only need a way to update(/refresh) the list automatically in all controllers. How can I achieve that.

I found these three links related but being a newbie I'm unable to figure my way out.

Community
  • 1
  • 1
vipanth
  • 85
  • 1
  • 7
  • If you're looking to keep multiple controllers in sync when data is fetched from the server, you'll want to keep the data itself in a service/factory. You can look up how to do this by googling `sharing data between angular controllers` – theaccordance May 23 '16 at 19:38
  • That means I have to change my logic. Isn't there any other way so that I can continue working with current structure?| – vipanth May 23 '16 at 19:44
  • @vipanth with your current structure you're a bit limited, since you're counting on the promise resolution, which is a one and done operation. You'd need an observable to continue receiving updated data. However, if you change the storage of the data to the service, then each controller could register a watcher on that data and update when the data changes. – David L May 23 '16 at 19:51
  • @DavidL Ok, I'll go with this suggestion using the second approach of the solution provided by Kobi – vipanth May 23 '16 at 19:57
  • I just wanted to mention you can actually do `return $http.get('../ListContacts')` since `$http` already returns a promise. using `defer` here is an anti-pattern. – Muli Yulzary May 23 '16 at 23:44

3 Answers3

0

You can do

Contacts.getData().then(function(data) {
    $scope.contactsBook = data;
    $scope.$emit('contacts:updated', data);
});

And then, where you need to notify the controller about the update:

$rootScope.$on('contacts:updated', function(e, contacts) {
  $scope.contacts = contacts;
});

Another approach

The service is holding the current contacts list

app.service('Contacts', function ($http,$timeout,$q) {
    this.currentList = [];
    this.getData = function() {
            var defer = $q.defer();
            $http.get('../ListContacts')
            .success(function(data) {
                defer.resolve(data);
            });
            return defer.promise;
        }
 });

In your controller:

Contacts.getData().then(function(data) {
        $scope.contactsBook = data;
        Contacts.currentList = data;
    });

In other controller:

controller('AnotherController', function($scope, Contacts) {
  $scope.contacts = Contacts.currentList;
});
Kobi Cohen
  • 678
  • 4
  • 9
  • Thanks. I will try the second approach. That looks more approachable. – vipanth May 23 '16 at 19:58
  • Your second approach will not update if the data updates a second time. You'd need a watcher in each controller. – David L May 23 '16 at 20:12
  • @Kobi Cohen I tried second approach. Contacts controller gets data, but AnotherController doesn't (despite the typo $scope.contacts instead of $scope.contactsBook). Did I miss anything? – vipanth May 23 '16 at 20:15
  • @DavidL Thanks, I figured that out (But currently I'm facing issue with getting data in **AnotherController**). I feel I can bind them with help of [link](http://stackoverflow.com/questions/18678225/angularjs-how-should-i-update-a-property-on-a-resolved-promise-inside-of-my-wat) this post. Do you have any other link that is more clear in this topic. Any help appreciated. – vipanth May 23 '16 at 20:23
0

While it is understandable that you may not want to update your current architecture, it may be necessary to adjust your calls slightly if you want to be able to easily share data between controllers via a service.

One flexible approach is to store the data in your service and register watchers in each controller. This allows you to call the service update from one controller (the Contacts controller) and have the change be reflected in all consuming controllers. Note the service is mocked.

You can find the working plunker example here.

Contacts Service:

var app = angular.module('app', []);    
app.service('contactsService', function ($http) {
    var contacts = [];

    return {
        loadData: function() {
            var mockGet = $q.defer();

            var data = [
              { id: 1, name: 'Jack' },
              { id: 2, name: 'Jill' }
            ];

            contacts = data;
            mockGet.resolve(contacts);
            return mockGet.promise; 
        },
        retrieveNewData: function() {
            var mockGet = $q.defer();

            var data = [
              { id: 1, name: 'Jack' },
              { id: 2, name: 'Jill' },
              { id: 3, name: 'Bob' },
              { id: 4, name: 'Susan' }
            ];

            contacts = data;
            mockGet.resolve(contacts);
            return mockGet.promise; 
        },
        getContacts: function () {
          return contacts;
        }
    }
});

Contacts Controller:

app.controller('ContactsCtrl', ['$scope', 'contactsService', 
  function ($scope, contactsService) {
    var vm = this;
    vm.contacts = [];

    vm.loadData = loadData;
    vm.retrieveNewData = retrieveNewData;


    $scope.$watch(angular.bind(contactsService, function () {
        return contactsService.getContacts(); 
      }), function (newVal) {
        vm.contacts = newVal;
    });


    function loadData() {
      contactsService.loadData();
    }

    function retrieveNewData() {
      contactsService.retrieveNewData(); 
    }
  }
]);

Other Controller:

app.controller('OtherCtrl', ['$scope', 'contactsService',
  function($scope, contactsService) {
     var vm = this;
     vm.contacts = [];

     $scope.$watch(angular.bind(contactsService, function () {
        return contactsService.getContacts(); 
      }), function (newVal) {
        vm.contacts = newVal;
    });
  }
]);
David L
  • 32,885
  • 8
  • 62
  • 93
-1

If you are going to return an object literal you will need to turn your .service() into a .factory() module . In this case I'll be using a service module .

Example

Your service .

app.service('Contacts', function ($http,$timeout,$q) {
   var Contacts = this;

        contacts.getData = function() {
            var defer = $q.defer();
            $http.get('../ListContacts')
            .success(function(data) {
                defer.resolve(data);
            });
            return defer.promise;
        }
    }

return Contacts;
 });

You will then need to inject this server into your ContactsController .

app.controller('ContactsController', function(Contacts){

$scope.data = null;

$scope.init = function(){
Contacts.getData().then(function(response){

$scope.data = response;

})
}

})

now data can be used in dom

Example

<li ng-repeat="x in data">{{x.name}}</li>
KpTheConstructor
  • 3,153
  • 1
  • 14
  • 22
  • Your first statement is actually incorrect. You can return an object literal from a service as well. See http://blog.thoughtram.io/angular/2015/07/07/service-vs-factory-once-and-for-all.html – David L May 23 '16 at 20:10
  • Also, your example doesn't solve for updated data. Nothing in your example actually will update the second time around. This is really no different from the OP's current state. – David L May 23 '16 at 20:11
  • Yes, I felt that the answer is leading me to few more questions – vipanth May 23 '16 at 20:24
  • Angular .factory returns and object literal representation of the module . Read the link you commented closely. angular services call a constructor function and the functions on the module itself can be returned . The point of this is ; if you have data that you want to continuously poll then its best angular practice to handle this in a service . Talking best practices , even though you can accomplish the same thing in a factory . – KpTheConstructor May 23 '16 at 21:02
  • Also if you want to continuously poll for changes , or "refresh" then you will need to watch for specific changes on the server to accomplish this or watch for a specific user action . This can be done in many different ways depending on your app . – KpTheConstructor May 23 '16 at 21:04