Unable to figure out what's happening here in $http.get - Angular - angularjs

I'm a newbie to Angular JS. I was referring Online tutorials and came across the $http service.
(function () {
"use strict";
angular
.module("ngClassifieds") // referring module which is already created
.controller("classifiedsCtrl", function ($scope, $http) {
$http.get('data/classifieds.json')
.then(function(classifieds){
$scope.classifieds = classifieds.data;
})
});
})();
In this piece of code, I'm not able to figure out these lines. Can
anyone explain what actually happens here ?
$http.get('data/classifieds.json')
.then(function(classifieds){
$scope.classifieds = classifieds.data;
}
I have this data in my data/classifieds.json file.
My question is, what exactly the data referred in classifieds.data is ?
what does classifieds.data represent here ?
what information does it contain?
what would be the result which we assign to $scope.classifieds?

$http returns a promise, it's an asynchronous call, angular use a fork of a library called Q for promises, you can see $q documentation here: https://docs.angularjs.org/api/ng/service/$q.
When the promise is fulfilled, that means, the asynchronous call is complete, the .then method call success or error callback depending on the result of the async call.
.then(successCallback, [errorCallback], [notifyCallback]) – regardless
of when the promise was or will be resolved or rejected, then calls
one of the success or error callbacks asynchronously as soon as the
result is available. The callbacks are called with a single argument:
the result or rejection reason. Additionally, the notify callback may
be called zero or more times to provide a progress indication, before
the promise is resolved or rejected.
The argument passed to the success callback is an object with the information about the request response. The data property contain the body of the response, in other way, all the content of data/classifieds.json file (in your case), therefore, $scope.classifieds will contain the json returned by data/classifieds.json.
Here a friendly article about promises and $q: http://www.dwmkerr.com/promises-in-angularjs-the-definitive-guide/

A simple get request example from AngularJS Docs.
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
In your code,
$http.get('data/classifieds.json') // This is the URI of your json file
.then(function(classifieds){ // Success callback
$scope.classifieds = classifieds.data; // Here, you are assigning response's data into $scope.classifieds
}

Related

Angular $http.reject vs. $q.reject

I am remember researching this awhile back and coming up empty handed and I still can't find any good info on this - why does there appear to be a $q.reject method but not an $http.reject method?
For example, in real life we might have:
unfollow: function (userId) {
if (!AuthService.isLoggedIn()) {
//$location.url('/login');
window.location.href = '/login';
return $q.reject({error: 'no logged-in user, but non-existent user could still click a follow button?'});
}
else {
return $http({
method: 'PUT',
url: ConfigService.api.baseUrl + '/v1/users/add_unfollow/by_id/' + userId
});
}
}
I would rather uses the relevant $http.reject instead of $q.reject, but that doesn't seem to work.
Since $http returns a promise in one leg of the conditional...the function itself needs to return a promise in the other leg also.
Using $q.reject() is simply a shortcut to return a rejected promise.
Without it, any place that calls unfollow().then() wouldn't have a then() method if a promise wasn't returned
$http would wrap the http calls and return a promise. The promise would rejected if the actual http request is failed, and adding a reject method would not make much sense, rather it should be in the promise.
In your example you would not even need to call the $http service to reject the request.

Restangular error interceptor - how to pass a custom object to a controller that does not know about restangular?

I am stuck with the approach I am taking probably due to my lack of knowledge about angular promises VS restangular promises, etc.
I have an AngularJs application with TypeScript (although typescript is mostly irrelevant here and the same applies to any javascript). These are the players:
controller: it gets injected a service, through this service the controller can send a POST to an API
service: it wraps restangular. The idea is that this service does not expose any restangular functionality to the controller. It abstracts the controller from knowing how to save an item. It has a method that accepts an object and returns an angular promise.
export interface IRegistrationService {
addRegistration(model: registration.BusinessRegistration): ng.IPromise<void>;
}
Restangular error interceptor: it handles Http Responses with status 400 coming from an API because they are validation errors and transforms them in a custom object. The idea is that eventually the controller can either succeed saving an item (posting it through the service) or get a validation error (that comes from this interceptor).
This is what I have so far:
The restangular error interceptor
restangularProvider.setErrorInterceptor((response: restangular.IResponse, deferred: ng.IDeferred<any>, responseHandler: any) => {
if (response.status === 400) {
let validationError: myTsd.IApiValidationErrors = getAsValidationError(response.data);
// How to pass this validationError as an errorCallback to the controller?
//deferred.notify(validationError);
//deferred.reject(validationError); //this stops the chain
//return true; // if error not handled. But where to put the validationError?
//return false; // if error handled. But where to put the validationError?
}
});
The service that abstracts the controller from knowing anything about restangular Notice that it should return an angular promise, not a restangular promise.
public addRegistration(model: registration.BusinessRegistration): ng.IPromise<void> {
return this.restangular.all("registration")
.post<registration.BusinessRegistration>(model)
.then(() => {
console.log("Registration posted successfully");
}, (error: any) => {
//if I get the object here, how to make it available in the errorCallback of the controller caller?
}, (notify: any) => {
//if I get the object here, how to make it available in the errorCallback of the controller caller?
});
}
The controller that uses that service but knows nothing about restangular
//public static $inject = ["app.services.RegistrationService"];
//.. controller code
this.registrationService.addRegistration(this.model)
.then(() => {
console.log("model posted successfully in remote API")
}, (error: myTsd.IApiValidationErrors) => {
// if there was any validation error I need the object here
console.log(error);
});
How should I chain everything? My "only" requirements are:
the logic to create that object is in a central place like the setErrorInterceptor, and it should distinguish between http responses 400 or any other. If the response is neither 2xx or 400 it can handle the error or pass it to the service that uses restangular. It doesn't matter
the service that uses restangular must allow the controller to either succeed or have a callbackError with the custom validation error object. It abstracts the controller from everything else.
Thanks a lot!
I don't fully understand the docs here https://github.com/mgonto/restangular#seterrorinterceptor and whether there is something else other than notifying or rejecting that I could do.
Restangular's .setErrorInterceptor() is a rather odd beast, which, as far as I can gather, won't do what you want it to do.
It can be made to sense error code(s) (eg your 400) and do stuff when that condition arises, but has no further ability other than to return false (block) or return anything else (not block).
The non-blocking action allows the promise chain to take its natural, unintercepted course.
The blocking action inhibits both the error path and the success path of the promise chain.
Therefore think of .setErrorInterceptor() as a "selective blocker", not a "filter" or a "catch", and contrast it with promise.catch() behaviour, by which :
an error state can be converted to success by returning some value/object,
the error can be rethrown, or some new error can be thrown, keeping the promise chain on the error path.
The inability of .setErrorInterceptor() to propagate anything other than the original error seems to mitigate against it in favour of a named "catch handler" (eg. getAsValidationError() or a function that wraps getAsValidationError()) that can be included wherever relevant. That should give you the feature you require.
The only problem I can foresee is getting the catch handler to recognise the "400" condition - possibly simple - requires research.
Don't get too hung up on Angular promises versus Restangular. They should inter-operate.

AngularJS then() behaves differently than success()-error() [duplicate]

This question already has answers here:
Why are AngularJS $http success/error methods deprecated? Removed from v1.6?
(2 answers)
Closed 4 years ago.
As the success() and error() functions are deprecated in AngularJS, I am updating my codes, replacing them with then(). Now according to my understanding, these two pieces of codes should behave identically:
$http
.get(/* some params */)
.success(function() {
// success cases here
})
.error(function() {
// error cases here
});
And
$http
.get(/* some params */)
.then(function() {
// success cases here
}, function() {
// error cases here
});
But in some cases I am getting different behavior. Can you please explain to me why would that happen, and more importantly, what would be the way to guarantee the identical behavior using then() functions?
The .success and .error methods ignore return values.
Consequently they are not suitable for chaining.
var httpPromise = $http
.get(/* some params */)
.success(function onSuccess(data, status, headers, config) {
var modifiedData = doModify(data);
//return value ignored
return modifiedData;
})
.error(function onError(data, status, headers, config) {
// error cases here
});
httpPromise.then(function onFullfilled(response) {
//chained data lost
//instead there is a response object
console.log(response.data); //original data
console.log(response.status); //original status
});
On the otherhand, the .then and .catch methods return a derived promise suitable for chaining from returned (or throw) values or from a new promise.
var derivedPromise = $http
.get(/* some params */)
.then(function onFulfilled(response) {
console.log(response.data); //original data
console.log(response.status); //original status
var modifiedData = doModify(response.data);
//return a value for chaining
return modifiedData;
})
.catch(function onRejected(response) {
// error cases here
});
derivedPromise.then(function onFullfilled(modifiedData) {
//data from chaining
console.log(modifiedData);
});
Response Object vs Four Arguments
Also notice that the $http service provides four arguments (data, status, headers, config) when it invokes the function provided to the .success and .error methods.
The $q service only provides one argument (response) to the functions provided to the .then or .catch methods. In the case of promises created by the $http service the response object has these properties:1
data – {string|Object} – The response body transformed with the transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
Chaining promises
Because calling the then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.2
Update
The .success and .error methods have been deprecated and removed from AngularJS V1.6.
For more information, see
Why are angular $http success/error methods deprecated? Removed from v1.6?.
One important difference is that you should probably use .then().catch() instead of .then(function(), function()). [1] The two-argument then (where the second function is the error handler) does not capture errors in the first function, as I understand it. IOW: $http.get().then(a, b) will not call b() if a() throws an error, where as $http.get().then(a).catch(b) will.
What other sorts of different behaviors are you getting?
1: standardized Promise API - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
The success function and the promise callback indeed receive different objects. As described in the docs, the promise callback receives a response object containing status, headers, data etc. The success function returns just the data field (the response body) of that object.

XHR.statusText error when aborting angular $http requests

So I have recently implemented a means to cancel an $http request via the timeout property by passing in and resolving a promise.
When a request is aborted the $http promise goes to the error block. This is all fine, its after this all finishes processing I receive an error from angular.js (xdomain.js is in the call stack) stating that it "Cannot read property 'statusText' of null". Null being the xhr object.
I'm at a loss currently as the request is successfully being cancelled, but it seems as if the xhr object is being destroyed right after calling xhr.onload().
Here's an example of how I'm cancelling the request:
var request = {
url: 'someurl',
method: 'get',
headers: headers
timeout: canceller // this is a promise that I resolve in a controller in order to abort an $http request. This works fine.
}
$http(request).then(function () {
//success block
}, function () {
// error block
});
As I said, it enters the error block but it isn't inside of that block that the angular error is thrown, rather it seems to be with the request comes back and there is no longer an xhr object waiting for it that an error occurs.
This is a common problem with Angular and Xdomain. A workaround I'm fond of is passing in a unique identifier or bool when submitting the request. If the value has changed when the request is completed simply ignore the results.

AngularJS Execute function after a Service request ends

I am using AngularJS Services in my application to retrieve data from the backend, and I would like to make a loading mask, so the loading mask will start just before sending the request. but how can I know when the request ends?
For example I defined my servive as:
angular.module('myServices', ['ngResource'])
.factory('Clients', function ($resource) {
return $resource('getclients');
})
.factory('ClientsDetails', function ($resource) {
return $resource('getclient/:cltId');
})
So I use them in my controller as:
$scope.list = Clients.query();
and
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
});
So the question would be, how to know when the query and get requests ends?
Edit:
As a side note in this question I've been using using angularjs 1.0.7
In AngularJS 1.2 automatic unwrapping of promises is no longer supported unless you turn on a special feature for it (and no telling for how long that will be available).
So that means if you write a line like this:
$scope.someVariable = $http.get("some url");
When you try to use someVariable in your view code (for example, "{{ someVariable }}") it won't work anymore. Instead attach functions to the promise you get back from the get() function like dawuut showed and perform your scope assignment within the success function:
$http.get("some url").then(function successFunction(result) {
$scope.someVariable = result;
console.log(result);
});
I know you probably have your $http.get() wrapped inside of a service or factory of some sort, but you've probably been passing the promise you got from using $http out of the functions on that wrapper so this applies just the same there.
My old blog post on AngularJS promises is fairly popular, it's just not yet updated with the info that you can't do direct assignment of promises to $scope anymore and expect it to work well for you: http://johnmunsch.com/2013/07/17/angularjs-services-and-promises/
You can use promises to manage it, something like :
Clients.query().then(function (res) {
// Content loaded
console.log(res);
}, function (err) {
// Error
console.log(err);
});
Another way (much robust and 'best practice') is to make Angular intercepting your requests automatically by using interceptor (see doc here : http://docs.angularjs.org/api/ng.$http).
This can help too : Showing Spinner GIF during $http request in angular
As left in a comment by Pointy I solved my problem giving a second parameter to the get function as following:
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
}, function(){
// do my stuff here
});

Resources