1

in my angular app I use a factory which get json data and pass it to a controller. it is working for me if I use simple json array but fails in a nested array in case of a simple json file I have this structure

[
          {
            "name": "bond_1",
            "profession": "Programmer",
            "hometown": "St Louis, MO"
          },
          {
            "name": "bond_2",
            "profession": "Salesman",
            "hometown": "Denver, CO"
          },
          {
            "name": "bond_3",
            "profession": "CEO",
            "hometown": "San Francisco, CA"
          }
]

my factory is this

.factory('Topology', function ($http){
 var data = [];
 return{
    get: function(){
        if (data.length == 0){
           $http.get("data.json") 
           .success(function (response){
               for(var i=0, ii=response.length; i<ii; i++){
                   data.push(response [i]);
               }
           });
        }
        return data;
    },
  }
});

and my controller is this

var installerControllers =angular.module('installerControllers', []);
installerControllers.controller('stageThreeCtrl', function ($scope,   Topology) {
       $scope.bonds=Topology.get();
})

now it all working fine and I can view the data when I doing ng-repeat on it from the view

but i need instead of the simple json structure use a nested array which looks like this

{
"bonds":[
        {
        "name": "Alex",
        "profession": "Programmer",
        "hometown": "St Louis, MO"
      },
      {
        "name": "David",
        "profession": "Salesman",
        "hometown": "Denver, CO"
      },
      {
        "name": "Laura",
        "profession": "CEO",
        "hometown": "San Francisco, CA"
      }
],
"networks":[
      {
      "name": "test",
      "all_hosts": "false",
      "IP_Version": "IPV4",
      "IP address": "10.10.10.10",
      "IPV net mask": "255.255.255.0",
      "Interface": "bond 0",
      "VLAN TAG": "4001",
      "Description": "some custom description"
    }
  ]
}

now I am trying to call for one of the objects from the controller in this way

var installerControllers =angular.module('installerControllers', []);
installerControllers.controller('stageThreeCtrl', function ($scope, Topology) {
   var data=Topology.get();
   $scope.bonds=data.bonds;  
})

but it is not working and I got in the console.log an empty array

your help will be very appreciated

7
  • 1
    it looks like an async issue, by the time your $scope.bonds = data.bonds gets executed, the get() has not been resolved, hence the empty array. Commented Jun 16, 2015 at 14:52
  • try to put a small delay between var data = Topology.get(); and the assignment $scope.bonds = data.bonds; in order to prove if it's an async issue Commented Jun 16, 2015 at 14:54
  • how can i add this delay? Commented Jun 16, 2015 at 14:57
  • 1
    As Jax says, there is an async issue, what you need to do is include $q into that factory and use that to project out the data. Commented Jun 16, 2015 at 14:57
  • 1
    Note, with your new data format, var data = []; should be var data = {};, and you shouldn't use .push, instead use assignment data[key] = response[key] Commented Jun 16, 2015 at 15:10

3 Answers 3

3

Your problem is NOT an asynchronous problem, you simply aren't referencing your new data format properly. I've substituted the $http call with a $timeout since I don't have a server to test against that returns your data. Since your data format has changed, you need to change the way you're referencing it in the factory, controller, and view.

var app = angular.module('app', []).factory('Topology', function ($timeout){
  var data = {}; // no longer an array
  
  return{
    get: function(){
        if (!data.bonds){
           $timeout(function () {
               data.bonds = [{name: 'Hello World!'}]; // this would be response.bonds
               data.networks = [{name: 'Hello World!'}]; // this would be response.networks   
           }, 2000);
        }
        return data;
    },
  }
}).controller('stageThreeCtrl', function ($scope, Topology) {
   var data=Topology.get();
   $scope.data = data; // renamed to data
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
  <p>wait 2 seconds...</p>
  <ul ng-controller="stageThreeCtrl">
    <li ng-repeat="bond in data.bonds">{{bond.name}}</li> <!-- using data.bonds -->
  </ul>
</div>


A better alternative would be to do some minor restructuring to instead take advantage of the promise returned from $http.

var app = angular.module('app', []).factory('Topology', function ($http){
  var promise;

  return{
    get: function(){
        if (!promise){
          promise = $http.get("data.json");
        }
        return promise;
    },
  }
}).controller('stageThreeCtrl', function ($scope, Topology) {
   Topology.get().then(function (response) {
     $scope.bonds = response.data.bonds;
   });
});
// your original view should now work
Sign up to request clarification or add additional context in comments.

5 Comments

This is a better answer. I'm not sure why everyone is using $q when $http itself returns a promise.
This is also correct. There is no problem in add $q also. I thought if he need any modification in response he has to use my answer. There is no reason to give me a down vote.
I tried it but it didn't worked i keep getting empty view in the html I tried bond in bonds and bond in response.bonds but it is still not working here a link to my page preview.c9.io/pnina_lorman/services/angular-seed/app/…
here is also an example that i did in plnker with your code plnkr.co/edit/kF3KpxKix3Vl5pb3hyBa?p=preview
Here it is working: plnkr.co/edit/py5OTeruEw2rkYkKzl8O?p=preview we were using response improperly (and the plunkr wasn't including script.js in the html)
1

Try this

   angular.module('app', []).factory('Topology', function ($http,$q){
 return{
    get: function(){
        var d = $q.defer();
           $http.get("data.json") 
           .success(function (response){ 
             console.log(response)
               d.resolve(response);
           });
        return d.promise; 
    },
  }
}).controller('stageThreeCtrl', function ($scope, Topology) {
  Topology.get().then(function (response) {

    $scope.bonds = response.bonds;  
  });
});

3 Comments

the response you geting is a json. So why every one push ing data
I tried it but it is not working for me please see a plnkr with the implementation plnkr.co/edit/6eQSDXqYfwpliBi6Wo9F?p=preview
can you pls acept the answer
-1

Try

Topology.get().then(function (data) {
    $scope.bonds=data.bonds;
});

As pointed out in the comments, I didn't notice that the get function wasn't returning a promise.

To return a promise, the $http API is based on the deferred/promise APIs exposed by the $q service, so just use the code below in the get function with the factory (service).

return $http.get('data.json');

2 Comments

that won't work because you are not returning a promise from the get function
hi i tried it but i am getting the error TypeError: Topology.get(...).then is not a function

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.