3

I have some parameters in the $rootScope as specified below:

myApp.factory('itemService', function($http) {
    return $http.get('/items');
});

myApp.run(function($rootScope, itemService) {
    itemService.success(function(response) {
        $rootScope.items = response;
    });
});

myApp.controller('displayCtrl', function($rootScope, $scope) {
    $scope.items = $rootScope.items;
});

When I run the above code, I get this error from firebug TypeError: $rootScope.items is undefined. I really do not know what is happening.

Here is a small addition. items is an array with a list of objects like this:

items = [
  {'name': 'spoon', 'price': 200},
  {'name': 'table', 'price': 400},
  {'name': 'shoe', 'price': 250}
];

I wish to make items available constantly in my app such that I can display each item on the item list (items) without making another request to the server. I intend to achieve this by simply displaying an item using $scope.item = items[$routeParams.id] each time I need to display an item. I look forward to implement this using either a function attached to ng-click or the normal #/route/:param mechanism. Thanks

C1pher
  • 1,933
  • 6
  • 33
  • 52
The Oracle
  • 2,373
  • 3
  • 26
  • 44
  • 1
    I'm assuming the itemService is a service that contains http calls for resources and returns a promised object when you invoke a method of the service, so where is the invocation? Shouldn't it be itemService.get().succes() and so on. Furthermore why are you putting data on the rootScope? This way you are polluting the rootScope. You can perfectly inject the itemService in the displayController? – skubski Aug 10 '15 at 13:05
  • The factory contains the `$http.get()` while run() contains the `.success`. Also I put data in the $rootScope because I want it to be available as soon as my file/app is loaded. – The Oracle Aug 10 '15 at 13:09
  • 1
    My bad, I didn't see the factory code at first. (I don't think it was available when I commented) In this case you're requesting a reference to an undefined (not yet set) object because the http get is still in progress. Either way you should upgrade your factory and get that data off the rootScope, it is bad practice and there is plenty of documentation why it is. – skubski Aug 10 '15 at 13:33
  • with the above method i have it done, just for the fact that `{{item.name}}` keeps appearing due to the above error. – The Oracle Aug 10 '15 at 15:43

4 Answers4

2

TypeError: $object.property is undefined is usually because a request to a reference of an object is made before that specific object (or its property) has been set. $http requests are asynchroneous by nature so other processes do not get blocked. It should be obvious that trying to make requests synchroneous could cause a major issue for people with very slow connections.

Apart from that, polluting the $rootScope is generally a bad idea. You can find a topic about global variables on the following link so that you investigate why the $rootScope is not such a good place.

Having said all that, it seems to me that you didn't want to make multiple requests to retrieve the same data. If so, you can use the cache option for $http.get methods.

e.g:

myApp.factory('itemService', function($http, $q) {
  return {
    get: function() {
      return $http({
        url: 'items.json',
        cache: true //keep the result in memory 
      });
    }
  };
})

myApp.controller('aCtrl', function(itemService) {
  var self = this;

  itemService.get().success(function(data) {
    self.items = data;
  });
});

myApp.controller('bCtrl', function(itemService) {
  var self = this;

  itemService.get().success(function(data) {
    self.items = data;
  });
});

This will make sure the information gets requested once and put into a cache. The data is accessible in different places.

  <div ng-controller="aCtrl as a">
    {{a.items}}
  </div>
  <div ng-controller="bCtrl as b">
    {{b.items}}
  </div>

This leaves me with another 'good' practice: the usage of the controllerAs syntax. Which provides a way to use namespaces in AngularJS.

Ofcourse, these are just tips and you should always consider the requirements!

Community
  • 1
  • 1
skubski
  • 1,586
  • 2
  • 19
  • 25
0

You run asynchronious method at run block :

 itemService.success(function(response){
    $rootScope.items = response;
});

But initialization goes on, so probably you access $rootScope.items before itemService succeed (or it fails, and you didnt predict such situation). I suggest you to do this (if you want to follow $rootScope convension.. which is bad by the way) :

 $rootScope.items = [];
 itemService.success(function(response){
    $rootScope.items = response;
 });
MariuszJasinski
  • 504
  • 3
  • 8
  • Please can you update me with the good or best way to do it. Best practice is the key to quality code. I so much appreciate. – The Oracle Aug 10 '15 at 13:12
0

You are setting items in the callback of an asynchronous process, so you are trying to access items on the $rootScope before its actually set.

If you are trying to initialize items when the controller is loaded, then there are other ways to do that such as using the resolve block of a route or manually calling the $http.get on the factory when the controller loads.

sma
  • 9,449
  • 8
  • 51
  • 80
0

Finally, I was able to come up with a solution. I realized that the problem was to have $rootScope.items available in displayCtrl at the same time it loads. But $rootScope.items is available in my view when my html page loads. So I simply passed the item id as a parameter and obtained it using $routeParams as follows

myApp.controller('displayCtrl', function($routeParams, $scope) {
    $scope.item_id = $routeParams.id; //given that the route looks like '/item/:id'
}); 

Then in my HTML file this what I did

<div ng-bind="items[item_id].name"></div>
<div ng-bind="items[item_id].price"></div>

This actual solved my problem.

The Oracle
  • 2,373
  • 3
  • 26
  • 44
  • Still, this is bad practice. You should use the `$rootScope` to store and share data. -1 – skubski Aug 28 '15 at 07:37
  • Thank you for siting that out. Please guide me on a standard way to achieve this without using `$rootScope`, storing and sharing data. – The Oracle Aug 28 '15 at 18:25
  • Ok, I have seen it, so you got a point, I ticked my answer because it is what I was looking for and I got it but I have voted up you point. thank you very much for the concern. I redesign my code to achieve the same result using your method. Then what exactly is the role of `$rootScope` in Angularjs? – The Oracle Aug 28 '15 at 18:39
  • 1
    The [documentation](https://docs.angularjs.org/api/ng/service/$rootScope) doesn't really specify what it should be used for but the tendancy is to use it for global variables (but even that could be solved differently with a [constant](https://docs.angularjs.org/api/ng/type/angular.Module#constant)) or to use it for event propagation/listening. Other that that, I pretty much leave the $root for what it is. – skubski Aug 28 '15 at 18:50
  • Thank you for sharing, I sincerely appreciate. – The Oracle Aug 28 '15 at 18:56