0

I can't access the JavaScript object's property even though the property is there. I am trying to access the Factory object's property in AngularJS. Below is the code:

angular.module("appManager")

  // factories.js
  .factory("DataFactory", function ($http) {
    var fac = {};

    fac.expenses = {};
    $http.get("mvc/m/revenue.json")
        .success(function (result) {
            console.log(result);            // line 9
            fac.expenses = result.expenses;
        });
    console.log(fac);                       // line 13
    return fac;
  })

  // expenseController.js
  .controller("expenseCtrl", function($scope, DataFactory){
    $scope.data = DataFactory.expenses;
    console.log(DataFactory);               // line 4
    console.log(DataFactory.expenses);      // line 5
    console.log($scope.data);               // line 6

    // Global // var d = {};
    d = DataFactory;
    e = DataFactory.expenses;
  });

And the ouput in the console: enter image description here

Someone also asked (here) about this but he did not get a solution. I also tried what was suggested there: using keys, the console throws "undefined function" error. You can also notice that the log() // 9 inside the log inside $http is called later than other function; can this be the reason? But notice the log() // 13 can already print the object.

I thought there may be something wrong with AngularJS, and I tried some global variables d and e, for testing purpose, which also provide the same result.

I also tried subscripts: DataFactory["expenses"] with same result

Can someone please tell me what I am doing wrong?

8
  • console.log will not print inherited properties Commented Jun 21, 2014 at 19:54
  • @Ven what is inherited properties? Commented Jun 21, 2014 at 19:57
  • If d.expenses has data but e doesn't, that means that somewhere d.expenses (or DataFactory.expenses) is replaced with another object, so it becomes different than e. Commented Jun 21, 2014 at 19:57
  • @Oriol d and e are used nowhere else, then how could they be overwritten? Commented Jun 21, 2014 at 19:59
  • This looks like the very common issue of the timing of an ajax response. The results of an ajax response must be used from within the success callback or passed to a function called from there. They can't be used elsewhere. fac.expenses is not valid outside of the .success handler where you set it. The other places you're trying to look at it, it likely has not yet been set. Commented Jun 21, 2014 at 20:03

2 Answers 2

2

You are using asynchronous calls, but trying to treat them like they happen synchronously. That simply doesn't work as you end up trying to use a result before the networking call that requested that result has actually finished and been returned.

Look at the order of the console statements. line 13 (after the $http.get()) call happens BEFORE line 9 (in the .success handler of the $http.get()). So, at line 13, there's no way that fac.expenses is assigned yet. Even the ones labelled lines 4,5,6 happen BEFORE fac.expenses is set after line 9.

It appears that you aren't writing your code to work with Asynchronous networking calls and that is why it isn't working properly. You can ONLY reliably use fac.expenses from within the .success handler or something that is called from the .success handler.

Also, since this is a timing related issue, you can be fooled in numerous ways by debugging tools. In general, you should log the actual value you're looking for, not a parent object because console.log() may confuse you when you later trying to drill into the parent object in the log (because it has since been filled in).


I don't quite follow exactly what you're trying to achieve here to know exactly what to recommend, but if you want to use fac.expenses, then you must either use it inside of the .success handler where it was initially returned or you can call some other function and pass fac.expenses to that other function. You can't return it from an async callback like you're doing and expect it to be available in other chained code that may have already executed.

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

1 Comment

Just got pinged by SO for 2.5K views and I can't believe I was this dumb 8 years ago lol. Promises!! @jfriend00 and others, thank you for being there to set me straight!
1

jfriend00 hit the nail on the head. You are returning an empty object from your DataFactory service, and then getting a result from the Ajax service. The nature of asynchronous calls is that your code continues to run, returning the empty object, while it waits for the response from the server. Async bites everyone the first time they run into it, but then you will recognise the pattern in the future.

There is a modern way of dealing with this called the Deferred Promise pattern. The $q service in Angular provides the deferred object for you.

https://docs.angularjs.org/api/ng/service/$q

So your service with $q would look something like this:

 .factory("dataFactory", ['$http', '$q', function ($http, $q) {
     var fac = $q.defer();

     $http.get("mvc/m/revenue.json")
         .success(function (result) {
             fac.resolve(result);
         });                   
      return fac.promise;
 }]);

and then your controller would look like

 .controller("expenseCtrl", ['$scope', 'dataFactory', function($scope, dataFactory){
    dataFatory.then(function(data) {
        $scope.data = data.expenses;
    });
 }]);

Comments

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.