Multiple $http calls in AngularJS - javascript

So here is what I need to do. I'm using the Angular Deferred Bootstrap library in my code because I need to receive some essential data from a RESTful server before bootstrapping and trying to load the content. Anyway, I must make a second call once the first call resolves. The second call is a login that depends on some URL that is contained in the first response.
Right now, I want to try to make the login call once I receive that data (I was trying in the .success() block) but once the first call resolves, the program begins bootstrapping before the login call is finished; things break because I'm not "logged in" on the server.
window.deferredBootstrapper.bootstrap({
element: window.document.body,
module: 'app',
resolve: {
STARTUP_CONFIG: ['$http', function($http) {
return $http.get(url1 + 'blah/blah/blah/' + layout);
}],
CONTEXT_CONFIG: ['$http', function($http) {
return $http.get(url1 + 'blah/blah/blah/blah').
success(function(data) {
$http.post('https://' + data.url2 + '/10001/guestidentity?client_id=' + data.id).
success(function(result){
token = result.token;
});
});
}],
}
});
Anyone have any idea what I can do?

hi since this is a promise you can chain the next call to the then function something like
$http(url).
then(function(response){
//note that this shouldn't be the body of the response
// but the entire response stream so you need to check for the status code and the body and apply and possibly apply any transformation that is needed as it will probably be just text
// then we can start the other call
return $http(url);
})
this way the second promise will be handled to the final destination.

Related

ng-repeat with directive: how to destroy repeated items after array has changed?

I have an angular js app, that uses an ng-repeat with a directive like this:
<div data-ng-repeat="n in items">
<div data-my-directive
item="n"></div>
</div>
where items is an array with integers.
Depending on actions of the user, the items array can be completely destroyed and made anew with new integers.
First time, it may be [1,2,4,9]
and next it may be [1,3,6,7]
for instance. This is dependent on some user choices.
The directive my-directive will perform some business logic server-side, so it will call the server as soon as it gets loaded. And then after a result returns, it shows a nice table to the users.
The problem is that some users don't wait until everything is nice and loaded and switch their view (which means the array changes). In this case, I see that the calls to the server are still being executed and that the result-function is still being called, even though the directive itself has been destroyed because the ngRepeat has rebound and all of the directives are re-made.
For instance:
$http.get(service.url + '/filters').success(function(result) {
alert(result);
});
This will display all of the alerts, even of the directives that are no longer on the page.
This poses a few problems. Can I destroy the directives in the repeat when the array changes or something like that to make sure that no logic is executed in a directive that shouldn't exist anymore (or that isn't displayed on the page anymore) ?
Or do you have some other ideas on how best to approach this?
Ideally, the directives should just disappear after the ng-repeat has rebound itself, so no logic is executed as soon as data comes back from the server.
When the user changes the parameters you can cancel the running request and start a new one.
In this Scott Allen's blog post you can find the detailed explanation of how this work.
You start creating a service or a factory with the method you will call:
var getData = function(){
var canceller = $q.defer();
var cancel = function(reason){
canceller.resolve(reason);
};
var promise =
$http.get(service.url + '/filters', { timeout: canceller.promise})
.then(function(response){
return response.data;
});
return {
promise: promise,
cancel: cancel
};
};
Then you call it in this way:
var request = service.getData();
$scope.requests.push(request);
request.promise.then(function(movie){
$scope.movies.push(movie);
clearRequest(request);
}, function(reason){
console.log(reason);
});
You then provide a method that will cancel the request:
$scope.cancel = function(){
var request = // retrieve the correct request from the requests array
request.cancel("User cancelled");
// Remove the request from the array
};
So I have a few thoughts for your question.
First you could use ng-cloak which is used to prevent the Angular html template from being briefly displayed by the browser in its raw (uncompiled) form while your application is loading. I find this very helpful if I want the user to wait until all the data has returned to view the page. ex.
<div id="template1" ng-cloak>{{ 'hello' }}</div>
Second you could try a resolve. A resolve contains one or more promises that must resolve successfully before the route will change. This means you can wait for data to become available before actually changing routes.
$routeProvider
.when("/news", {
templateUrl: "newsView.html",
controller: "newsController",
resolve: {
message: function(messageService){
return messageService.getMessage();
}
}
})
The directive needs to use the $destroy event to cancel operations in progress.
app.directive("myDirective", function() {
return {
controller: function($scope, $http, $q) {
var canceller = $q.defer();
var cancel = function(reason){
canceller.resolve(reason);
};
$http.get(url, { timeout: canceller.promise})
.then(function(response){
$scope.data = response.data;
});
$scope.$on('$destroy', function () {
cancel("Scope destroyed");
});
}
}
});
When the ng-repeat removes an item, it destroys the scope of the item. Use the $destroy event to cancel asynchronous operations in progress.
From the Docs:
The $destroy() method is usually used by directives such as ngRepeat for managing the unrolling of the loop.
Just before a scope is destroyed, a $destroy event is broadcasted on this scope. Application code can register a $destroy event handler that will give it a chance to perform any necessary cleanup.
--AngularJS $rootScope.scope API Reference -- $destroy

Angular JSONP Factory call not working, however it does work in controller

I have looked at every post on SO related to this issue but still cannot find the answer. This response appeared to be the most promising but I didn't understand the answer, and it appears the person who asked did not either.
What am I trying to do?
Make a JSONP angular GET request to the itunes API. This works perfectly when I make the GET request from my controller, however when I tried to refactor, in order to obey the principle of 'Separation of Concerns' by moving this request into a factory, and injecting into the controller I am running into difficulties. I know the call is still being made as I am getting a CORS error if I do not use a JSONP call in my factory.
Here is the factory code:
var myApp = angular.module('jDillaApp', []).factory('Music', function ($http) {
var o = {
songs: []
};
o.getNextSongs = function () {
return $http.jsonp({
method: 'GET',
url: 'https://itunes.apple.com/search?term=j+dilla&limit=25?format=jsonp&callback=JSON_CALLBACK'
}).success(function (data) {
console.log(data)
});
}
return o
})
My controller looks like this:
myApp.controller('jDillaCtrl', ['$scope', '$http', 'Music',
function ($scope, $http, Music) {
Music.getNextSongs();
$scope.songs = Music.songs;
var media = new Audio();
$scope.playSong = function () {
media.pause();
var randomSong = Math.round(Math.random() * ($scope.songs.length - 1));
media = new Audio($scope.songs[randomSong]);
media.play();
}
}]);
The error is not a lot of help as far as I can tell but worth posting anyway TypeError: h.replace is not a function
You $http.jsonp method has wrong inputs that was your first problem, $http.jsonp method accepts two parameter as such
$http.jsonp(url, [config])
url -> the url which you want make an ajax
config -> if you want to make additional changes in request then you can pass those setting from here in {} key value pair like you can set headers, type, etc.
Your service method should return promise, as you only need to return $http.jsonp object which already has promise, no need of creating extra promise object here.
Service
o.getNextSongs = function () {
return $http.jsonp('https://itunes.apple.com/search?term=j+dilla&limit=25?format=jsonp&callback=JSON_CALLBACK')
}
In controller that call will be resolved using .success & .error also you can use .then
Controller
Music.getNextSongs()
.success(function (data) {
//here you get the ajax success
console.log(data)
});

Changing outside variables from a function

I use cordova and ionic for my mobile application.
I trying to use $http.get() for loading a JSON web service, so I wrote this code :
.controller('loading', function ($scope, $state, $http) {
var loadedService = {
news: false,
users: false
};
$http.get('http://{url}/users')
.success(function (result) {
loadedService.users = result;
});
I want to change the loadedService.users to returned result from webservice.
But when I trying to console.log(loadedService.users) I will get false (the default value for this variable).
What should I do?
$http.get, or javascript in general is asynchronous. Put console.log inside the success callback of http.get, you will see that (hopefully), you are getting correct results there. But if you put console.log after the get call, you will see the false output since your get call is still busy but your js keeps running. Long story short, js is asynchronous.

Using resource in controller before its loaded?

I'm currently working on an angular application i'll post some sample code here.
In My Service i have a method that access a rest api via $resource:
function getData() {
resource.get({}, function success(data) {
return data;
}, function error() {
console.log('error')
})
}
Now over in a controller that has been injected with the service i attempt to use the data with some conditions from the previous page that are passed through as a routeParameter as follows:
$scope.data = MyResource.getData()
$scope.editingObject = angular.copy($scope.data[$routeParams.objIndex])
On my view i would like to bind to $scope.editingObject but since that code executes immediately and the data may not have arrived back from the server i receive the following error:
TypeError: Cannot read property '0' of undefined
Now i understand that i cannot access the array before the data has arrived back from the server but my question is more about how do i construct this service in such a way that i can access that data?
To resolve this issue i have attempted to place a watch on $scope.data and monitor for the data to arrive but it never fires when the data does arrive. i have confirmed this using console.log in a set timeout to check after a few seconds if the data has arrived at which point it has... and the object is updated to reflect this.
Could anyone please point me in the right direction regarding services, resources and accessing the data?
I would like to see the $watch() code because it should fire when data is received. But anyways, you should use the promise returned from service:
MyResource.getData().then(function(data) {
$scope.data = data;
$scope.editingObject = angular.copy($scope.data[$routeParams.objIndex])
});

Show spinner on multiple $http calls angularJs

This is the deal.
I what to show a spinner when doing a $http call, but the problem here is that I have multiple calls at ones, so the examples I found here didn't help.
Did anyone have a solution for this?
A way to stack the calls so the spinner remains until the last call finish? I hope to make my point.
Im doing this.
angular.module('moduleName', []).
factory.("SomeService", function () {
return:{
getResources(params) {
/* do the $http call */
}
}
}).
controller("SomeCtrl", function (SomeService) {
SomeService.getResources(params)
}).
controller("OtherCtrl", function (SomeService) {
SomeService.getResources(params)
});
The 2 controllers may call the service at the same time and the may get diferent responce.
All $http calls in Angular return a promise.
The $q service doesn't have all the bells and whistles of the Q library, which it is based, but if you look at the docs, it does have an all method that can be used to give you the functionality you want.
Here's how you could use it:
app.controller('HttpController', function($http, $q) {
// A hypothetical submit function
$scope.submit = function() {
// Set a loading variable for use in the view (to show the spinner)
$scope.loading = true;
var call1 = $http.get(/* ... */);
var call2 = $http.get(/* ... */);
var call3 = $http.get(/* ... */);
$q.all([call1, call2, call3]).then(function(responses) {
// responses will be an array of values the individual
// promises were resolved to. For this case, we don't
// need it, since we only care that they all resolved
// successfully.
$scope.loading = false;
}, function(errorValue) {
// If any of the promises is rejected, the error callback
// will be resolved with that rejection value, kind of like
// an early exit. We want to mark the loading variable
// as false here too, and do something with the error.
$scope.loading = false;
});
};
});
Use a variable that is initialized to the number of calls you are making. In each of the callbacks for the AJAX calls, call a function that decrements the value of this variable and if it's zero, removes the spinner.
num_calls = 3;
function considerRemoveSpinner() {
if (--window.num_calls == 0)
{
removeSpinner();
}
}
$http.get("url").success(
function() {
/* regular handler */
considerRemoveSpinner();
}
);
/* other ajax calls */
You can do this by using interceptor and passing one flag in the header of each API.What all you need to do it.Add one parameter in each $http call.
Than capture it in the interceptor Request method by using property config.headers.{{Parameter}}.On the basis of this flag broadcast one event for showing wait image.

Categories