Leapahead
Leapahead

Reputation: 63

Angularjs - Multiple $http REST calls (second call dependent on output of first)

I am a newbie to AngularJs world - I am trying to get data from two REST WS calls.

First one returns a set of data (works fine)- using the value of data from frist one I need to make another webservice call and retrive the data and print in a table.

Below is what I have tried so far: HTML:

<table ng-table="tableParams" id="request_table" class="table table-striped table-bordered" cellspacing="0" width="100%">
<thead>
    <tr>
        <th>Number</th>
        <th>Price</th>
        <th>Short Description</th>
        <th>Requested for</th>
        <th>State</th>
    </tr>
</thead>
<tbody>
    <tr ng-repeat="request in req_list | orderBy:sortType:sortReverse | filter: searchItem">
        <p ng-repeat="item in fetchRequest(request.price)"></p>

        <td>{{request.number }}</td>
        <td>{{request.price}}</td>
        <td>{{request.short_description}}</td>
        <td>{{request.requested_for.display_value}}</td>
        <td>{{request.stage}}</td>
    </tr>

</tbody>

Script:

            angular.module('sc_request_list')
            .controller('MyRequestItems', [
                '$scope',
                '$http',
                function($scope, $http) {
                    $scope.sortType = 'number' //set the default sort type
                    $scope.sortReverse = false; // set the default sort order
                    $scope.searchItem // set the default search/filter term
                    $scope.itemUrl = '/api/sc_request';
                    $scope.reqUrl = '/api/sc_req_item';
                    $http.defaults.headers.common.Accept = "application/json";
                    $scope.fetchRequestItemList = function() {
                        $http({
                            method: 'GET',
                            url: $scope.itemUrl,
                        }).


                        success(function(data, status) {
                            var result = data;
                            var json = JSON.stringify(result);
                            var data = JSON.parse(json);
                            $scope.req_list = data.result; // response data 

                        }).
                        error(function(data, status) {
                            $scope.req_list = [{
                                "req_list": "Error fetching list"
                            }];
                        });

                    }

                    $scope.fetchRequest = function(request) {
                        console.log("Request Number is: " + request);
                        $http({
                            method: 'GET',
                            url: $scope.reqUrl + "/" + request,
                        }).


                        success(function(data, status) {
                            var result = data;
                            var json = JSON.stringify(result);
                            var data = JSON.parse(json);
                            $scope.req = data.result; // response data 

                        }).
                        error(function(data, status) {
                            $scope.req = [{
                                "req": "Error fetching list"
                            }];
                        });

                    }
                }


            ]);

Any help much appreaciated.

Upvotes: 6

Views: 24743

Answers (5)

Deblaton Jean-Philippe
Deblaton Jean-Philippe

Reputation: 11388

I would do it like that, with the second call inside the success response.

function getFirstItem(){           // Here I declare a function that will return a promise.
    return $http({
        method: 'GET',
        url: $scope.itemUrl
    });
}

function getDependantItem(){         // I declare a second function that returns  a promise
    return $http({
        method: 'GET',
        url: $scope.otherUrl
    });
}

$scope.fetchRequest = function(request) {      // Here is a function that can be called by  the view.
    getFirstItem()
        /*
         * I call the first function that returns a promise. 
         * the "then" is just setting a 
         * callback that will be executed when your server will respond.
         * The next thing the code does, after registering the callback,
         * is going to the end of your function,
         * and return nothing.
         */
     .then(function(result1){
        $scope.req = result1.result; // Now that the server has answered, you can assign the value to $scope.req, and then, call the second function.

        getDependantItem().then(function(result2){
            // This is still an async call. The code keeps on running, and
            // you will only have this callback running when your server will have responded

            // Handle your response here and assign your response to a $scope variable.
        }, function(error2){
             // If an error happened during the second call, handle it here.
        });

    }, function(error1){ 
        // If an error happened during first call, handle it here.
    });

    // If you want to return something, you must do it here.
    // Before your promises would ever be resolved
    // That's the reason why you got an undefined
};

Few things can be noted about your code :

  • Avoid .success and .error, use .then(function(success){}, function(error){});. The 2 firsts are deprecated from most of the frameworks, and the angular doc doesn't speak about it at all anymore.
    https://docs.angularjs.org/api/ng/service/$q

  • Avoid putting everything in the $scope. Just put things that need to be shared with the view, or other controllers.

  • ASAP, learn about services to share functionalities, and have 1 layer of your application responsible for 1 thing

Upvotes: 5

tombarti
tombarti

Reputation: 426

Your calls are made asynchronously so while the calls are being made, the rest of the code is still executing. Thus,

<p ng-repeat="item in fetchRequest(request.price)"></p>

is executed before request.price is even defined.

What you need to do is chain the calls to your api calls:

$http({
  method: 'GET',
  url: $scope.itemUrl,
}).


success(function(data, status) {
  var result = data;
  var json = JSON.stringify(result);
  var data = JSON.parse(json);
  $scope.req_list = data.result; // response data 
  
  //Loop through requests
  $scope.req_list.foreach(function (elem, index) {
    //chained call is here
    $scope.fetchRequest(elem.price, index);
  });
}).

error(function(data, status) {
  $scope.req_list = [{
    "req_list": "Error fetching list"
  }];
});

$scope.fetchRequest = function(request, index) {
  console.log("Request Number is: " + request);
  $http({
    method: 'GET',
    url: $scope.reqUrl + "/" + request,
  }).

  success(function(data, status) {
    var result = data;
    var json = JSON.stringify(result);
    var data = JSON.parse(json);
    //Attach result of api call to apropriate element of req_list array
    $scope.req_list[index].items = data;

  }).
  error(function(data, status) {
    $scope.req = [{
      "req": "Error fetching list"
    }];
  });

}
}

And then change:

<p ng-repeat="item in fetchRequest(request.price)"></p>

To:

<p ng-repeat="item in request.items"></p>

Upvotes: 1

murnax
murnax

Reputation: 1222

With $q service, you can chain your promise objects like this:

$scope.fn1 = function(){
  var deferred = $q.defer();
  $http.({ method: 'GET', url: 'YOUR_1st_API' }).success(function(data){
    deferred.resolve(data);
  });
  return deferred.promise;
}

$scope.fn2 = function(data){
  var deferred = $q.defer();
  $http.({ method: 'GET', url: 'YOUR_2nd_API' }).success(function(data){
    deferred.resolve(data);
  });
  return deferred.promise;
}

// after $scope.fn1() is done pass data with resolve method to $scope.fn2() , now you can access data from fn1 inside fn2
$scope.fn1().then($scope.fn2);

And it would be better if you separate your business logic like data fetching to "service" or "factory" and inject them to your controller. With this way your code will be a lot easier to read and maintain.

More info for $q service

Upvotes: 3

michelem
michelem

Reputation: 14590

Inside the first success call you should call the second one:

angular.module('sc_request_list')
    .controller('MyRequestItems', [
        '$scope',
        '$http',

        function($scope, $http) {
            $scope.sortType = 'number' //set the default sort type
            $scope.sortReverse = false; // set the default sort order
            $scope.searchItem // set the default search/filter term
            $scope.itemUrl = '/api/sc_request';
            $scope.reqUrl = '/api/sc_req_item';
            $http.defaults.headers.common.Accept = "application/json";
            $scope.fetchRequestItemList = function() {
                $http({
                    method: 'GET',
                    url: $scope.itemUrl,
                }).
                success(function(data, status) {
                    var result = data;
                    var json = JSON.stringify(result);
                    var data = JSON.parse(json);
                    $scope.req_list = data.result; // response data

                    // Second call
                    $scope.fetchRequest = function(request) {
                        console.log("Request Number is: " + request);
                        $http({
                            method: 'GET',
                            url: $scope.reqUrl + "/" + request,
                        }).
                        success(function(data, status) {
                            var result = data;
                            var json = JSON.stringify(result);
                            var data = JSON.parse(json);
                            $scope.req = data.result; // response data 

                        }).
                        error(function(data, status) {
                            $scope.req = [{
                                "req": "Error fetching list"
                            }];
                        });

                    }

                }).
                error(function(data, status) {
                    $scope.req_list = [{
                        "req_list": "Error fetching list"
                    }];
                });

            }
        }


    ]);

Upvotes: -1

RedSparkle
RedSparkle

Reputation: 579

Usually I use following pattern:

$http({method: 'GET',
       url: '<*whatever url is, no need to put it on $scope, unless you really need it there*>'})
    .success(function(result) { return result.data; })
    .error(function(reason) { <handle your error here> })
    .then(function(data) { <make your second request here with the data you've got from the first> });

Upvotes: -1

Related Questions