Search code examples
javascriptangularjsservicecontrollers

Share data between controllers AngularJs with Services


Background

I am making Service which has a list to be shared between two controllers. To start I followed this tutorial on Services:

And I managed to successfully create and execute the basic tutorial on Plunker:

Problem

The problem here, is that when I click the form button, I need to make an HTTP GET request to my server, and update the service list when I get the response.

To achieve this, I first tried using the following Plunker modification:

The jest of the code can be seen in the service:

  // Create the factory that share the Fact
  app.factory('ListService', function($http) {
    var list = {};
    list.data = [];

    list.request = function(theHairColor) {
      var theUrl = "https://gnome-shop-fl4m3ph03n1x.c9users.io/api/v1/gnomes?hairColor=" + theHairColor;
      console.log(theUrl);
      $http({
        method: 'GET',
        url: theUrl,
        headers: {
          'Content-Type': 'application/json; charset=utf-8'
        }
      }).then(function successCallback(response) {
        list.data = response.data.entries; //does not work
        console.log(response.data.entries);
      }, function errorCallback(response) {
        console.log('Error: ' + response);
      });
    };

    return list;
  });

If you tried it out, you will see it simply doesn't work, and I don't really understand why. In a previous question I made, someone explained to me that it was related to references, and that I should replace list.data = response.data.entries; //does not work for the following:

//this works but is rather flimsy ....
list.data.length = 0;
Object.assign(list.data, response.data.entries);

Which in deed does work, but I find it rather counter intuitive:

Another suggestion was also given, in that I should change my gnomeList controller to :

app.controller("gnomeList", function(ListService) {
    var self = this;
    self.listService = ListService;
  });

and then iterate over the service's list directly:

<div ng-controller="gnomeList as listCtrl">
      <p ng-repeat="gnome in listCtrl.listService.data">{{ gnome.id }}: {{ gnome.name }}</p>
</div>

Which also works, but attaches the controller directly to the service:

Questions:

  1. Are there any other ways to make my first code sample (that didn't work) work?
  2. Which of these solutions would be preferable and why? (which one is more Angulary?)

Solution

  • Problem is you initially copy the data in your gnomeList and it is passed by value.

    app.controller("gnomeList", function(ListService) {
      var self = this;
      self.list = ListService.data;
    });
    

    When your controller gets initialized here, it puts a copy of ListService.data into self.list. However, when updating the values in the services, this controller does not get initialized again and therefore the value is not updated.

    Objects in javascript are passed by reference. Just like you said, you could directly put the service on scope to use its data or you simply set the properties on an object before you set them on your scope. (Plunkr)

    Javascript

    app.controller("gnomeList", function(ListService) {
      var self = this;
      self.list = ListService.value; // value is an object
    });
    
    // Create the factory that share the Fact
    app.factory('ListService', function($http) {
      var list = {};
      list.value = {};
    
      list.request = function(theHairColor) {
        var theUrl = "https://gnome-shop-fl4m3ph03n1x.c9users.io/api/v1/gnomes?hairColor=" + theHairColor;
        console.log(theUrl);
        $http({
          method: 'GET',
          url: theUrl,
          headers: {
            'Content-Type': 'application/json; charset=utf-8'
          }
        }).then(function successCallback(response) {
          list.value.data = response.data.entries; // extend value object
        }, function errorCallback(response) {
          console.log('Error: ' + response);
        });
      };
    
      return list;
    });
    

    HTML

    <div ng-controller="gnomeList as listCtrl">
      <p ng-repeat="gnome in listCtrl.list.data">{{ gnome.id }}: {{ gnome.name }}</p>
    </div>
    

    Moreover it is better to use built in angular.extend for extending objects.