How to $http Synchronous call with AngularJS

HttpAngularjs

Http Problem Overview


Is there any way to make a synchronous call with AngularJS?

The AngularJS documentation is not very explicit or extensive for figuring out some basic stuff.

ON A SERVICE:

myService.getByID = function (id) {
    var retval = null;

    $http({
        url: "/CO/api/products/" + id,
        method: "GET"
    }).success(function (data, status, headers, config) {

        retval = data.Data;

    });

    return retval;
}

Http Solutions


Solution 1 - Http

Not currently. If you look at the source code (from this point in time Oct 2012), you'll see that the call to XHR open is actually hard-coded to be asynchronous (the third parameter is true):

 xhr.open(method, url, true);

You'd need to write your own service that did synchronous calls. Generally that's not something you'll usually want to do because of the nature of JavaScript execution you'll end up blocking everything else.

... but.. if blocking everything else is actually desired, maybe you should look into promises and the $q service. It allows you to wait until a set of asynchronous actions are done, and then execute something once they're all complete. I don't know what your use case is, but that might be worth a look.

Outside of that, if you're going to roll your own, more information about how to make synchronous and asynchronous ajax calls can be found here.

I hope that is helpful.

Solution 2 - Http

I have worked with a factory integrated with google maps autocomplete and promises made​​, I hope you serve.

http://jsfiddle.net/the_pianist2/vL9nkfe3/1/

you only need to replace the autocompleteService by this request with $ http incuida being before the factory.

app.factory('Autocomplete', function($q, $http) {

and $ http request with

 var deferred = $q.defer();
 $http.get('urlExample').
success(function(data, status, headers, config) {
     deferred.resolve(data);
}).
error(function(data, status, headers, config) {
     deferred.reject(status);
});
 return deferred.promise;

<div ng-app="myApp">
  <div ng-controller="myController">
  <input type="text" ng-model="search"></input>
  <div class="bs-example">
     <table class="table" >
        <thead>
           <tr>
              <th>#</th>
              <th>Description</th>
           </tr>
        </thead>
        <tbody>
           <tr ng-repeat="direction in directions">
              <td>{{$index}}</td>
              <td>{{direction.description}}</td>
           </tr>
        </tbody>
     </table>
  </div>

'use strict';
 var app = angular.module('myApp', []);

  app.factory('Autocomplete', function($q) {
    var get = function(search) {
    var deferred = $q.defer();
    var autocompleteService = new google.maps.places.AutocompleteService();
    autocompleteService.getPlacePredictions({
        input: search,
        types: ['geocode'],
        componentRestrictions: {
            country: 'ES'
        }
    }, function(predictions, status) {
        if (status == google.maps.places.PlacesServiceStatus.OK) {
            deferred.resolve(predictions);
        } else {
            deferred.reject(status);
        }
    });
    return deferred.promise;
};

return {
    get: get
};
});

app.controller('myController', function($scope, Autocomplete) {
$scope.$watch('search', function(newValue, oldValue) {
    var promesa = Autocomplete.get(newValue);
    promesa.then(function(value) {
        $scope.directions = value;
    }, function(reason) {
        $scope.error = reason;
    });
 });

});

the question itself is to be made on:

deferred.resolve(varResult); 

when you have done well and the request:

deferred.reject(error); 

when there is an error, and then:

return deferred.promise;

Solution 3 - Http

var EmployeeController = ["$scope", "EmployeeService",
        function ($scope, EmployeeService) {
            $scope.Employee = {};
            $scope.Save = function (Employee) {                
                if ($scope.EmployeeForm.$valid) {
                    EmployeeService
                        .Save(Employee)
                        .then(function (response) {
                            if (response.HasError) {
                                $scope.HasError = response.HasError;
                                $scope.ErrorMessage = response.ResponseMessage;
                            } else {
                                
                            }
                        })
                        .catch(function (response) {
                           
                        });
                }
            }
        }]
 

var EmployeeService = ["$http", "$q",
            function ($http, $q) {
                var self = this;
    
                self.Save = function (employee) {
                    var deferred = $q.defer();                
                    $http
                        .post("/api/EmployeeApi/Create", angular.toJson(employee))
                        .success(function (response, status, headers, config) {
                            deferred.resolve(response, status, headers, config);
                        })
                        .error(function (response, status, headers, config) {
                            deferred.reject(response, status, headers, config);
                        });
    
                    return deferred.promise;
                };

Solution 4 - Http

I recently ran into a situation where I wanted to make to $http calls triggered by a page reload. The solution I went with:

  1. Encapsulate the two calls into functions
  2. Pass the second $http call as a callback into the second function
  3. Call the second function in apon .success

Solution 5 - Http

Here's a way you can do it asynchronously and manage things like you would normally. Everything is still shared. You get a reference to the object that you want updated. Whenever you update that in your service, it gets updated globally without having to watch or return a promise. This is really nice because you can update the underlying object from within the service without ever having to rebind. Using Angular the way it's meant to be used. I think it's probably a bad idea to make $http.get/post synchronous. You'll get a noticeable delay in the script.

app.factory('AssessmentSettingsService', ['$http', function($http) {
    //assessment is what I want to keep updating
    var settings = { assessment: null };
    
    return {
        getSettings: function () {
             //return settings so I can keep updating assessment and the
             //reference to settings will stay in tact
             return settings;
        },
        updateAssessment: function () {
            $http.get('/assessment/api/get/' + scan.assessmentId).success(function(response) {
                //I don't have to return a thing.  I just set the object.
                settings.assessment = response;
            });
        }
    };
}]);

    ...
        controller: ['$scope', '$http', 'AssessmentSettingsService', function ($scope, as) {
            $scope.settings = as.getSettings();
            //Look.  I can even update after I've already grabbed the object
            as.updateAssessment();

And somewhere in a view:

<h1>{{settings.assessment.title}}</h1>

Solution 6 - Http

Since sync XHR is being deprecated, it's best not to rely on that. If you need to do a sync POST request, you can use the following helpers inside of a service to simulate a form post.

It works by creating a form with hidden inputs which is posted to the specified URL.

//Helper to create a hidden input
function createInput(name, value) {
  return angular
    .element('<input/>')
    .attr('type', 'hidden')
    .attr('name', name)
    .val(value);
}

//Post data
function post(url, data, params) {

	//Ensure data and params are an object
	data = data || {};
	params = params || {};

	//Serialize params
	const serialized = $httpParamSerializer(params);
	const query = serialized ? `?${serialized}` : '';

	//Create form
	const $form = angular
		.element('<form/>')
		.attr('action', `${url}${query}`)
		.attr('enctype', 'application/x-www-form-urlencoded')
		.attr('method', 'post');

	//Create hidden input data
	for (const key in data) {
		if (data.hasOwnProperty(key)) {
			const value = data[key];
			if (Array.isArray(value)) {
				for (const val of value) {
					const $input = createInput(`${key}[]`, val);
					$form.append($input);
				}
			}
			else {
				const $input = createInput(key, value);
				$form.append($input);
			}
		}
	}

	//Append form to body and submit
	angular.element(document).find('body').append($form);
	$form[0].submit();
	$form.remove();
}

Modify as required for your needs.

Solution 7 - Http

What about wrapping your call in a Promise.all() method i.e.

Promise.all([$http.get(url).then(function(result){....}, function(error){....}])

According to MDN

> Promise.all waits for all fulfillments (or the first rejection)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFlavio CF OliveiraView Question on Stackoverflow
Solution 1 - HttpBen LeshView Answer on Stackoverflow
Solution 2 - HttpallelView Answer on Stackoverflow
Solution 3 - HttpSrinivasView Answer on Stackoverflow
Solution 4 - Httpvikas agarthaView Answer on Stackoverflow
Solution 5 - HttpBluebaronView Answer on Stackoverflow
Solution 6 - HttpAdam ReisView Answer on Stackoverflow
Solution 7 - HttpManjit DosanjhView Answer on Stackoverflow