user3054684 user3054684 - 1 month ago 18
HTTP Question

Angular nested Promise shows an error

I have 2 API calls.

The second API call depends on the Property ID returned to make the second API call to check if each of these properties has parking.

If it does, then I add details of that property to an object and push the object into an Array.

The second API call is nested inside the first. After I've looped through all the properties, I check if the Array length is more than 0, if it is then I can display the returned properties in page, else it shows an error.

The problem is even when there are properties returned with parking, the else statement or error function executes, as well as displaying properties on the page.

Is there a way to complete the nested Promise before checking if my Array is more than 0?

Here's my code:



$scope.viewPropertyList = function(latlong) {
$scope.locationError = false;
var latlongArray = latlog.split('::');
var searchLat_scope = latlongArray[0];
var searchLon_scope = latlongArray[1];

if (searchLat_scope && searchLon_scope) {
var data = Property.getAllProperties({
dest: 'property',
apikey: API_KEY,
lat: encodeURIComponent(searchLat_scope),
limit: 10,
lon: encodeURIComponent(searchLon_scope)
}).$promise.then(function(success) {
var propertyMarkers = [];
$scope.dbMarkers = 0;
for (var i = 0, l = success.property.length; i < l; i++) {
(function(i) {
Property.getProperty({
dest: 'property',
propertyId: success.property[i].name,
apikey: API_KEY
}).$promise.then(function(propertyData) {

for (var j = 0, k = propertyData.services.length; j < k; j++) {
if (propertyData.services[j].name === "parking") {
var obj = {
"propertyName": success.property[i].propertyName,
"telephone": success.property[i].telephone,
"postcode": success.property[i].address.postcode,
"city": success.property[i].address.city,
"county": success.property[i].address.county,
"addressLine1": success.property[i].address.addressLine1
};
propertyMarkers.push(obj);
}

}

if (propertyMarkers.length != 0) {
$scope.dbMarkers = propertyMarkers;
$scope.selectedLat = searchLat_scope;
$scope.selectedlog = searchLon_scope;

} else {
$scope.locationErr = true;
$scope.errorMsg = "No properties found";
}

});

})(i);
}
}, function(error) {
$scope.locationErr = true;
$scope.errorMsg = "Something went wrong, please try again";
});
}
}




Answer

Two main things :

  • there's no attempt to aggregate multiple promises generated in a loop.
  • the if (propertyMarkers.length > 0) {...} else {...} is too deeply nested.

Minor :

  • the inner iteration can break as soon as 'parking' is found. If it continued and further 'parking' was found, then duplicate markers would be created.
$scope.viewPropertyList = function(latlong) {
    $scope.locationError = false;
    var latlongArray = latlog.split('::');
    var searchLat_scope = latlongArray[0];
    var searchLon_scope = latlongArray[1];

    if (searchLat_scope && searchLon_scope) {
        Property.getAllProperties({
            dest: 'property',
            apikey: API_KEY,
            limit: 10,
            lat: encodeURIComponent(searchLat_scope),
            lon: encodeURIComponent(searchLon_scope)
        }).$promise.then(function(success) {
            var propertyMarkers = [];
            $scope.dbMarkers = 0;

            // create an array of promises by mapping the array `success.property`.
            var promises = success.property.map(function(prop) {
                return Property.getProperty({
                    dest: 'property',
                    propertyId: prop.name,
                    apikey: API_KEY
                }).$promise.then(function(propertyData) {
                    for (var j=0, k=propertyData.services.length; j<k; j++) {
                        if (propertyData.services[j].name === 'parking') {
                            propertyMarkers.push({
                                'propertyName': prop.propertyName,
                                'telephone': prop.telephone,
                                'postcode': prop.address.postcode,
                                'city': prop.address.city,
                                'county': prop.address.county,
                                'addressLine1': prop.address.addressLine1
                            });
                            break; // 'parking' is found - no point iterating further
                        }
                    }
                });
            });

            /* ******** */
            // Aggregate `promises`
            $q.all(promises).then(function() {
                // This block is now un-nested from its original position, 
                // and will execute when all `promises` have resolved.
                if (propertyMarkers.length > 0) {
                    $scope.dbMarkers = propertyMarkers;
                    $scope.selectedLat = searchLat_scope;
                    $scope.selectedlog = searchLon_scope;
                } else {
                    $scope.locationErr = true;
                    $scope.errorMsg = 'No parking found';
                }
            });
            /* ******** */

        }).catch(function(error) {
            $scope.locationErr = true;
            $scope.errorMsg = 'Something went wrong, please try again';
        });
    } else {
        $scope.locationErr = true;
        $scope.errorMsg = 'Problem with lat/lng data';
    }
}

Notes :

  • that the outer iteration is now coded as success.property.map(), which returns promises and avoids the need for an IIFE.
  • Extra error handling added