winter sun
winter sun

Reputation: 582

access json nested array from factory angular

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

Upvotes: 1

Views: 1452

Answers (3)

Kevin B
Kevin B

Reputation: 95017

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

Upvotes: 3

simon
simon

Reputation: 1485

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;  
  });
});

Upvotes: 1

Sean.W
Sean.W

Reputation: 43

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');

Upvotes: -1

Related Questions