3

Background

I am making a angularJS app

I have an object called a Map. The Map has an array of Transforms IDs.

I need to fetch each transform in the Maps's array, then save these to an array of Transforms instead of an array of Transform IDs. So far, I have this done, except calling the function at the correct time.

I want to call a function: doStuffWithTransformArray using the completed array.

Question

How do I call a function only when the array is finished being filled?

Current Code (this only works sometimes)

    $scope.makeTransformArray = function () {
        $scope.transforms = [];
        $scope.map.transforms.forEach(function(eachTransformID, index) {
            Transform.get({uuid: eachTransformID.uuid}, function(transformResult) {
                $scope.transforms[index] = transformResult;
                if ($scope.transforms.length == $scope.map.transforms.length) {
                    $scope.doStuffWithTransformArray($scope.transforms);
                    return;
                }
            });
        });
    }

Problem

This current code will work only when the last Transform in the Map's array finishes last.

It will call the doStuffWithTransformArray twice if when the array is filled like this: (5) [y, y, y, empty, y]

The fourth one is not yet filled but the fifth finished.

EDIT 1

Here is my transform Service:

angular.module('serviceregistryApp')
.factory('Transform', function ($resource) {
    var url = 'api/v1/transform/:uuid';
    return $resource(url, {transform: '@transform',  uuid: '@uuid'}, {
        'save': {method: 'POST', headers: {"Authorization": "ABC"}},
        'delete': {method: 'DELETE', headers: {"Authorization": "ABC"}},
        'post': {method: 'POST', headers: {"Authorization": "ABC"}},
        'get': {
            method: 'GET', isArray: false, 
            headers: {
                "Authorization": "ABC"
            }, url: "api/v1/transform/:uuid",
            transformResponse: function (data) {
                return JSON.parse(data);
            }
        }
    });
});

EDIT 2

As suggested in the comments, this seems to work, but I find it kinda ugly.

    $scope.makeTransformArray = function () {
        $scope.transforms = [];
        var counter = 0;
        $scope.map.transforms.forEach(function(eachTransformID, index) {
            Transform.get({uuid: eachTransformID.uuid}, function(transformResult) {
                $scope.transforms[index] = transformResult;
                counter = counter + 1;
                if (counter == $scope.map.transforms.length) {
                    $scope.doStuffWithTransformArray($scope.transforms);
                    return;
                }
            });
        });
    }
georgeawg
  • 48,608
  • 13
  • 72
  • 95
Rorschach
  • 3,684
  • 7
  • 33
  • 77
  • It seems like the easiest thing to do would be to increment a counter every time a Transform finishes, then call your `doStuff` function when the counter equals your expected length. – Austin Mullins Jun 25 '18 at 18:13
  • 2
    As a hint when working with AngularJS, not everything needs to be attached to $scope. $scope should really only be worked with when you have the need to attach something directly to the browser; i.e. click events and direct bindings. All of the other work can be done in normal JavaScript functions totally outside of a scoped event. It makes things much easier to understand when you're trying to figure out a complex problem. – Chris Jun 25 '18 at 18:21
  • Is it possible to change the implementation of ```Transform.get(...)```? If you could make it return a promise resolving ```transformResult``` await/async can be used here to make sure the call of ```doStuffWithTransformArray(...)``` will happen only if the for-each loop is done. – Bee Jun 25 '18 at 18:31
  • @DavidMichaelHuber Tranform.get is implemented by Angular's built in CRUD library $resource. I have added the service in EDIT 1 – Rorschach Jun 25 '18 at 18:33

1 Answers1

0

NgResource instances have an attached property named $promise that can be used for transforming results after they arrive from the server:

$scope.object = Transform.get({uuid: eachTransformID.uuid});
var promise = $scope.object.$promise;
var newPromise = promise
  .then(function(object) {
     var obj2 = transform(object);
     $scope.object = angular.copy(obj2);
     return obj2;
}).catch(function(errorResponse) {
     console.log("ERROR", errorResponse.status);
     throw errorResponse;
});
$scope.object.$promise = newPromise;

Use the promise for subsequent chaining or consolidation with $q.all.

georgeawg
  • 48,608
  • 13
  • 72
  • 95