neha tj
neha tj

Reputation: 97

AngularJS : calling a factory method within the loop

I have a factory method which looks like below:

angular.module('GridSamplesApp')
.factory('registerPostFactory', function($resource, $q, WebRequest) {
    var getMessage = function(Upddata, token, dataurl)
    {
            var deferred = $q.defer();
             var settings = {
                data: Upddata,
                headers: {
                  'Content-Type': 'application/JSON',
                  'x-csrf-token' : token
                },
                method: 'POST',
                url: dataurl,
                withCredentials: true
            };
            WebRequest.requestRaw(settings).then(
    function(response) {
      // data from app sec

      var msg = response;

     deferred.resolve(msg);
    },
    function(error) {
      console.log('Error retrieving message', error);
      deferred.reject('Error retrieving message', error);
    });

  return deferred.promise;
    };


  return {
   getMessage: getMessage
  };
   });

I have a controller which looks like

$scope.getLogs = function()
{

  $.each($scope.modifiedSN, function(i, e) {
      if ($.inArray(e, result) == -1) result.push(e);
  });

  $scope.inputs.push({SN:'', Key:'', status:'', log:''});
  for(var i=0; i<result.length; i++)
  {
    var j = result[i];
    if ($scope.data[j].SerialNumber !== "")
    {


  var Upddata = {};
  Upddata['IConvRuleFlg'] = '';
  Upddata['SrNum'] = $scope.data[j].SerialNumber;
  Upddata['LvEmailId'] = '[email protected]';
  Upddata['WKey'] = $scope.data[j].WtyKey;

            registerPostFactory.getMessage(Upddata, $scope.token, dataurl).then(
                function(response) {
                  $scope.msg = response.headers["custommessage"];
                  $scope.data[j].AutolinkErrorlog = $scope.msg;
                  $scope.inputs.push({SN: $scope.data[j].SerialNumber, Key: $scope.data[j].WtyKey, status: response.headers["msgtype"], log: $scope.msg});
                },
                function(error) {
                    console.log('Error reading msg: ', error);
                  }
                  );
          }
  }

};

The issue with this is that it only takes the last element in the array since it is an asynchronous call and the loop will not wait for the response, I tried using $q.all() but could not figure out how to implement this, can anyone please help?

Upvotes: 1

Views: 944

Answers (2)

mido
mido

Reputation: 25044

From what I understand, your factory works ok, and like @RaviMone said, it is with the usage of async callback code within for loop, you ll be surprised how often beginners fall into that trap. Also, I see a $scope.msg, not sure where it comes from and how it works, but due to async and parallel nature of your calls, it might be display wrong values for various calls, if it changes per call, you should think about serializing your call.

A cleaner way to write $scope.getLogs might be( I have reduced the usage of jQuery, used ES5 stuff, if you have to support legacy systems, you can use this ):

$scope.getLogs = function(){
  var result = [] // again not sure where the result comes from, so initizing it here, else you can append filtered array to the previous set
  $scope.modifiedSN.forEach(function(value) {
      if (result.indexOf(value) < 0) result.push(e);
  });
  $scope.inputs.push({SN:'', Key:'', status:'', log:''});

  var promises = result.map(function(val){
    return $scope.data[val];
  }).filter(function(val){
    return val && val.SerialNumber !== "";  // first check if $scope.data[j] exists
  }).map(function(val){
    return registerPostFactory.getMessage({
        IConvRuleFlg: '',
        LvEmailId: '',
        WKey: val.WtyKey,
        SrNum: val.SerialNumber
    }).then(function(response){
        $scope.msg = response.headers["custommessage"];
        val.AutolinkErrorlog = $scope.msg;
        $scope.inputs.push({SN: val.SerialNumber, Key: val.WtyKey, status: response.headers["msgtype"], log: $scope.msg});
    }).catch(function(e){
        console.log('Error reading msg: ', e);
    });
  });

  $q.all(promises)
    .then(function(resArray){
        console.log('get all logs...');
    }).catch(function(e){
        console.log('some error: ', e);
    });
};

Edit:

if you want them done in sequence:

$scope.getLogs = function(){
  var result = [] // again not sure where the result comes from, so initizing it here, else you can append filtered array to the previous set
    , serialPromise = $q.when(1);   // just initializing a promise.
  $scope.modifiedSN.forEach(function(value) {
      if (result.indexOf(value) < 0) result.push(e);
  });
  $scope.inputs.push({SN:'', Key:'', status:'', log:''});

  result.map(function(val){
    return $scope.data[val];
  }).filter(function(val){
    return val && val.SerialNumber !== "";  // first check if $scope.data[j] exists
  }).forEach(function(val){
    var datum = {
            IConvRuleFlg: '',
            LvEmailId: '',
            WKey: val.WtyKey,
            SrNum: val.SerialNumber
    };
    serialPromise = serialPromise.then(function(){  // adding a new promise to the chain.
        return registerPostFactory.getMessage(datum);
    }).then(function(response){
        $scope.msg = response.headers["custommessage"];
        val.AutolinkErrorlog = $scope.msg;
        $scope.inputs.push({SN: val.SerialNumber, Key: val.WtyKey, status: response.headers["msgtype"], log: $scope.msg});
    }).catch(function(e){
        console.log('Error reading msg: ', e);
    });
  });

  serialPromise.then(function(){
        console.log('got all logs...');
    }).catch(function(e){
        console.log('some error: ', e);
    });
};

Upvotes: 1

RONE
RONE

Reputation: 5485

Here you have to use closure, I have modified your code,

(function(data) {
    //console.log(data) //You can notice here, you are getting all individual loop objects
    var Upddata = {};
    Upddata['IConvRuleFlg'] = '';
    Upddata['SrNum'] = data.SerialNumber;
    Upddata['LvEmailId'] = '[email protected]';
    Upddata['WKey'] = data.WtyKey;

    registerPostFactory.getMessage(Upddata, $scope.token, dataurl).then(
        function(response) {
            $scope.msg = response.headers["custommessage"];
            $scope.data[j].AutolinkErrorlog = $scope.msg;
            $scope.inputs.push({
                SN: data.SerialNumber,
                Key: data.WtyKey,
                status: response.headers["msgtype"],
                log: $scope.msg
            });
        },
        function(error) {
            console.log('Error reading msg: ', error);
        }
    );
})($scope.data[j]);

Upvotes: 0

Related Questions