I'm developing web app with angular.js, I'm currently a little confused about what's the proper way to handle errors. In my app, I have used ngResource to call rest API of server. So I'll have a lot of ngResource api calls.
e.g. user resource, there're user.query( ), user.get( ) , user.save( ) ......
Do I suppose to put an error callback into all of the ngResource api calls?
Just to handle all kinds of errors: like server down or no internet access ??
I just don't think put an error callback in every ngResource api call is a good idea. That'll produce a lot of redundant code and make my code not neat .
What will you do to handle various error types?
You can use an interceptor and do whatever you want when an error occured :
var app = angular.module("myApp", []);
app.config(function ($provide, $httpProvider) {
$provide.factory('ErrorInterceptor', function ($q) {
return {
responseError: function(rejection) {
console.log(rejection);
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('ErrorInterceptor');
});
With this interceptor you can read the status code and do what you need (a perfect use case is to redirect your user to a login page if status code is 401).
Since ngResource use $http, your interceptors will also be executed when you call a resource method.
Of course, you can do more and add an interceptor before / after a request is made.
See the full documentation here : http://docs.angularjs.org/api/ng.$http
See this fiddle : http://jsfiddle.net/4Buyn/ for a working sample.
Related
I have a simple angular resource that I've defined as below:
CompanyService.factory('CompanyService',
function ($resource) {
return $resource('https://baseurl.com/api/values/');
}
);
I then have a controller that calls that resource passing in a success and fail function:
.controller('companyList', function($scope, CompanyService) {
$scope.companies = CompanyService.query(
function(data) {
console.log(data);
return data;
},
function(error){
console.log("Error:");
console.log(error);
}
);
The rest API is a .NET MVC Web API that is extremely basic. I've configured it to return JSON and it simply returns an array of two objects like below. I've also enabled CORS so my angular app, which is hosted in a different domain, can call the api.
[{ID:1, Name:"TEST1"}, {ID:2, Name:"TEST2"}]
I've tested the REST call using jquery and just straight call through browser. All was functional (including the cross site scripting when calling from my angular app just using a straight JavaScript HTTP call).
When I try to call the api from my controller however, it always ends up in the error function. The error object contains a data property that is always populated with the string "resource is required|resource is required|undefined"
When I check the network I see no call to the values end point. It's as if the call is failing before ever being made.
If I change out the url to point to some sample REST api like https://jsonplaceholder.typicode.com/users/ it works fine and I'm able to see the call to "users" in the network traffic, which makes me think there is something wrong with my C# REST endpoint, however all my tests to call the REST endpoint outside of angular work successfully.
Can anyone help? I can't find anyone reporting this issues before anywhere on the net.
should the code be the one below? i didn't test it, just guess.
myModule.factory('CompanyService',
function ($resource) {
return $resource('https://baseurl.com/api/values/');
}
)
.controller('companyList', function($scope, CompanyService) {
CompanyService.query(
function(data) {
$scope.companies = data;
console.log(data);
return data;
},
function(error){
console.log("Error:");
console.log(error);
}
);
I ended up rebuilding my angular app from scratch. My first app was from the angular-seed github and had a handful of libraries already added in for testing and other things. One of those things is was was leading to this error as once I started a new project completely from scratch and added in angular and my REST call things worked perfectly. I've already spent too much time working through this so not going to spend any more time identifying exactly what it is but in case anyone else runs into the problem I did want to answer this one and close the book on it.
I'm using Restangular interceptor to handle errors with status code as 412,409 etc.
But I'm not getting status in the requestInterceptor function.
I have declared the Restangular RequestInterceptor using Typescript.
In my app.ts file,
app.run(Restangular){
Restangular.setErrorInterceptor(function(response) {//code to handle error});
}
Error:
Restangular provides an option to access a full response access, probably setting up this might help. By using Restangular.setFullResponse(true) you should be able to access the status code in your interceptor.
So, I guess according to your snippet now it could look like this(since there is where you are injecting Restangular if you have any other configuration section that where the setting should go)
app.run(Restangular){
Restangular.setFullResponse(true);
Restangular.setErrorInterceptor(function(response) {//code to handle error});
}
It's important to point out that setting up this option, now the access to your responses will be sightly different for all your services unless you create something to handle the next .data behavior:
function(response){
var result = response.data;
var statusCode = result.status;
var responseData = result.myApiResponse;
}
For more information please check: https://github.com/mgonto/restangular#setfullresponse, and hope my post helps.
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
});
What is the difference between expect and when in the ngMock AngularJS module?
They both provide a response, so when would you use one over the other?
I read the docs at angularJS.com, but it was not very clear to me.
This is the service I want to test using Jasmine, so should I expect a endpoint was called, or should I bank on a known value being returned?
(function () {
'use strict';
var app = angular.module('cs');
app.service('PlateCheckService', ['$http', function ($http) {
return {
checkPlate: function (plateNumber) {
return $http.post('PlateCheck/Index', {
plateNumber: plateNumber
}).then(function (response) {
return {
message: response.data.VehicleAtl === null ? 'Clean' : 'Hot',
alertClass: response.data.VehicleAtl === null ? 'alert-success' : 'alert-danger'
};
});
}
};
}]);
}());
The explanation in the doc is crystal clear to me:
Request Expectations vs Backend Definitions
Request expectations provide a way to make assertions about requests made by the application and to define responses for those requests. The test will fail if the expected requests are not made or they are made in the wrong order.
Backend definitions allow you to define a fake backend for your application which doesn't assert if a particular request was made or not, it just returns a trained response if a request is made. The test will pass whether or not the request gets made during testing.
So, if you use when(), you can do any request, in any order, and the test won't fail. If you use expect(), then the test will fail if the backend doesn't receive the expected requests, in the same order as the expected ones.
I am fairly new to AngularJS and am trying to learn some best practices. I have things working, but would like to start adding some unit tests to my modules and controllers. The first one I am looking to tackle is my AuthModule.
I have an AuthModule. This Module registers a Factory called "AuthModule" and exposes things like "setAuthenticatedUser" and also fields like "isLoggedIn" and "currentUser". I think this is a fairly common pattern in an AngularJS application, with some variations on the specific implementation details.
authModule.factory(
'AuthModule',
function(APIService, $rootScope) {
var _currentUser = null;
var _isLoggedIn = false;
return {
'setAuthenticatedUser' : function(currentUser) {
_currentUser = currentUser;
_isLoggedIn = currentUser == null ? false : true;
$rootScope.$broadcast('event:authenticatedUserChanged',
_currentUser);
if (_isLoggedIn == false) {
$rootScope.$broadcast('event:loginRequired')
}
$rootScope.authenticatedUser = _currentUser;
$rootScope.isLoggedIn = _isLoggedIn;
},
'isLoggedIn' : _isLoggedIn,
'currentUser' : _currentUser
}
});
The module does some other things like register a handler for the event "loginRequired" to send the person back to the home screen. These events are raised by the AuthModule factory.
authModule.run(function($rootScope, $log, $location) {
$rootScope.$on("event:loginRequired", function(event, data) {
$log.info("sending him home. Login is required");
$location.path("/");
});
});
Finally, the module has a run block which will use an API service I have to determine the current logged in user form the backend.
authModule.run(
function(APIService, $log, AuthModule) {
APIService.keepAlive().then(function(currentUser) {
AuthModule.setAuthenticatedUser(currentUser.user);
}, function(response) {
AuthModule.setAuthenticatedUser(null);
});
});
Here are some of my questions:
My question is how would you setup tests for this? I would think that I would need to Mock out the APIService? I'm having a hard time because I keep getting unexpected POST request to my /keepalive function (called within APIService.keepAlive())?
Is there any way to use $httpBackend in order to return the right response to the actual KeepAlive call? This would prevent me from having to mock-out the API service?
Should I pull the .run() block out which obtains the current logged in user out of the AuthModule and put it into the main application? It seems no matter where I put the run() block, I can't seem to initialize the $httpbackend before I load the module?
Should the AuthModule even be its own module at all? or should I just use the main application module and register the factory there?
Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the service have been configured and the injector has been created. Run blocks typically contain code which is hard to unit-test, and for this reason should be declared in isolated modules, so that they can be ignored in the unit-tests.angularjs docs
I suggest you take a look at this authentication service, using a service is the way to go.
Hopefully this would help ... Good luck