I'm trying to use apiSearch.get().then(func(){...}) I have it working in my tests, but I can't get the data when I use that same method in my controller. Is it even possible to use .then() with a returned resource? If not, is there another solution I can use in my in place of my controller or test code?
service.js
angular.module('app').factory('apiSearch', ['$resource', '$log',
function ($resource, $log) {
return $resource('api/search/:value', {}, {
get: {
method: 'GET',
isArray: false,
cache: true
}
});
}
])
pageService.js
/*** this works when I test it in the browser but makes my test fail ***/
apiSearch.get({value: 'thisvalue'}, function (res) {
$state.go('storefront.home');
}, function (err) {
$state.go('not-found');
})
/*** this works for my test but fails in the browser ***/
apiSearch.get({value: 'thisvalue'}).then(function (res) {
$state.go('storefront.home');
}, function (err) {
$state.go('not-found');
})
test.js
it("Should ", function(){
resolveData = 'itemName'
deferred.resolve(resolveData);
spyOn(apiSearch, "get").and.returnValue(deferred.promise)
$scope.$digest();
expect($state.current.name).toEqual('storefront.home');
});
Related
I have an AngularJS service for a restful API:
angular
.module('app', [
])
.service('api', ['$http', '$q', function APIService($http, $q) {
this.get = function (dataProperty, params) {
return $http({
method: 'get',
url: 'https://some.api/rest/',
params: angular.extend({
default_params...
}, params)
})
.then(
function (result) {
if (result.data.status === 'ok') {
return result.data[dataProperty];
} else {
return $q.reject(angular.extend(new Error(result.data.message), { result: result.data }));
}
},
function (reason) {
return $q.reject(angular.extend(new Error('AJAX request to the API failed'), { reason: reason.data }));
});
};
}]);
I'm trying to test this api.get with the following:
describe('api', function () {
var
$httpBackend,
service;
beforeEach(module('app'));
beforeEach(inject(function (_$httpBackend_, _api_) {
$httpBackend = _$httpBackend_;
service = _api_;
}));
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('', function () {
$httpBackend
.when('get', 'https://some.api/rest/')
.respond({
data: {
status: 'ok'
}
});
service.get('status', {});
$httpBackend.flush();
$httpBackend
.expect('get', 'https://some.api/rest/');
});
});
But I'm getting the error callback every time:
Error: AJAX request to the API failed in bower_components/angular-mocks/angular-mocks.js (line 279)
Am I going about setting up the test correctly? I believe the .when and .response is used to fake the actual $http call, but I can't get the success callback to fire.
The two issues were .when not looking for the correct URL (because get params were thrown in I needed to make it a regex:
.when('GET', /https:\/\/api\.flickr\.com\/services\/rest\/.*/)
Then, the .respond doesn't need to be padded with a data object, it does that for you:
.respond({ stat: 'ok' });
Hi I am trying to pull my angular js factory data to my controller,
please have a look if there is any issue.
factory.js
.factory('History', ['$http', '$q', function ($http, $q) {
function history () {
// angular.extend(SearchOptions.getDefaults(), params, options);
var deferred = $q.defer();
$http({
method: 'GET',
url: '/res/orders/' + 31536427 + '/history-group'
})
.success(function (res) {
// console.log(res);
})
.error(function (err) {
// TODO error handler
deferred.reject(err);
});
return deferred.promise;
}
return {
history: history
};
}]);
controller.js
.controller('HistoryCtrl', ['$scope', '$state', '$stateParams', 'History', function($scope, $state, $stateParams, History) {
History.history().then(function(res) {
console.log(res);
$scope.history = res.body;
console.log($scope.history);
}, function(err) {
// TODO error handler
console.log(err);
})
.finally(function(err) {
});
}]);
You need to pass the response in the success function in the 'History' factory as below:
.success(function (res) {
// console.log(res);
deferred.resolve(res);
})
The issue with your code is you are not resolving the promise after getting the data in the success callback function. Resolve it as shown below in the .success callback function :
deferred.resolve(res);
Few points to improve your code:
$http service in Angular by default returns a promise. Hence, you don't have to explicitly construct a promise using $q which is an anti pattern (Deferred anti-pattern). Just returning $http object from the service itself will do the
job. Doing return $http() is equivalent to return deferred.promise() in your code.
.success and .error callbacks are deprecated in the latest version(1.6) of AngularJs (Deprecation Notice). The disadvantage of using these is that they are not chainable as they ignore return values. Hence, it is better to use .then instead.
Applying above changes, your service can be refactored to below :
.factory('History', ['$http', function ($http) {
function history () {
return $http({
method: 'GET',
url: '/res/orders/' + 31536427 + '/history-group'
})
.then(successCallback, errorCallback);
}
function successCalback (res) {
return res;
}
function errorCalback (err) {
return err;
}
return {
history: history
};
}]);
I'm using angularjs 1.5.8.
I get this error when I'm trying to cancel an http request with angular :
$cancelRequest is not a function
My code :
app.factory('User', function($resource) {
var getUsersResource = $resource(
'/users',
null,
{get : {method: 'GET', isArray: true, cancellable: true}}
);
return {
getUsers : function() {
return getUsersResource.get({},
function(data) {
...
}, function(error) {
...
}
);
}
};
});
app.controller('InitController', function($rootScope, User, ...) {
...
User.getUsers();
...
}
app.factory('AuthInterceptor', function($q, $location, $injector) {
return {
responseError: function(response) {
if (response.status === 401) {
$injector.get('$http').pendingRequests.forEach(
function (pendingReq) {
pendingReq.$cancelRequest();
}
);
$location.path('login');
}
return $q.reject(response);
}
};
});
Do you know how I can solve this error ?
Thanks
The documentation suggests that $cancelRequest should be used with the resource object. From my initial review, it appears that you're correctly using $resource within the User factory. But, I'm not sure about how you're implementing this within the AuthInterceptor factory. It doesn't look like you're using User.getUsersSources() at all. Therefore, I believe the reason that you're getting that error is because you're not using $cancelRequestion correctly. That being said, you might have forgotten to include other parts of the code.
Ideally, the resolved $resource object from User.getUserResources() should be passed into AuthInteceptor.
I think that you should declare your service like that:
.factory('categoryService', ['$resource', function($resource) {
return $resource('/', {},
{
'get': {
'method': 'GET',
'cancellable': true,
'url': '/service/categories/get_by_store.json',
},
});
}])
And when you use this service, it should be called so:
if ( $scope.requestCategories ) {
$scope.requestCategories.$cancelRequest();
}
$scope.requestCategories = categoryService['get']({
}, function(res){
//some here
}, function(err){
//some here
});
I guess I miss something. Have spent some time trying to understand why my test is not working.
The code.
angular.module('services')
.factory('UserPreferencesService', ['$resource', function ($resource)
{
return $resource('/rest/user-preferences/:id', {},
{ getById: { method: "GET", params: { id:'#id'}} });
}
]);
The test:
it('should get by Id', function() {
//given
var preferences = {language: "en"};
httpBackend.whenGET('/rest/user-preferences/1').respond(preferences);
//when
service.getById( {id:1} ).$promise.then(function(result) {
console.log("successssssssssssssssssssssssssssssssssssss");
//then
expect(result).toEqual(preferences);
}, function() {
console.log("something wrong");
})
});
It never triggers: "successssssssssssssssssssssssssssssssssssss".
What did I miss?
There were some things wrong and other things missing in your code.
The main problem was that you were not calling the flush() function that emulates the response from the server, so the $promise was never resolved. Also, bear in mind that when the promise gets resolved, the response that you get it's a promise, meaning that this: expect(result).toEqual(preferences); won't work, but this: expect(result.lang).toEqual(preferences.lang); will.
Here you have a fixed version of your code:
The service:
angular.module('services',['ngResource'])
.factory('UserPreferencesService', ['$resource', function ($resource)
{
return $resource('/rest/user-preferences/:id', {},
{ getById: { method: "GET", params: { id:'#id'}} });
}
]);
The Test:
describe('My Suite', function () {
var httpBackend, service;
beforeEach(module('services'));
beforeEach(inject(function (_$httpBackend_, UserPreferencesService) {
httpBackend = _$httpBackend_;
service = UserPreferencesService;
}));
describe('My Test', function () {
it('should get by Id', function() {
var preferences = {language: "en"};
var result = {};
httpBackend.whenGET('/rest/user-preferences/1').respond(preferences);
service.getById({id:1}).$promise.then(function(result_) {
result = result_;
});
expect(result.language).not.toEqual(preferences.language);
httpBackend.flush();
expect(result.language).toEqual(preferences.language);
});
});
});
Working Example
I would like to add to this that I had the same issue, but was using the callback, as follows:
httpBackend.whenGET('/rest/user-preferences/1').respond(function(method, url, data) {
//do something with method, url, data...
return 200;
});
It never got hit until i replaced return 200, by return [200]. Apparently it needs an array to return.
I feel this info could be made more explicit in the documentation.
I have coded the following service:
angular.module('App')
.factory('TestService', ['$http', '$q', '$resource', function (
$http, $q, $resource) {
var TestResource = $resource('/api/Tests', {}, {
saveData: { method: 'PUT' },
deleteData: { method: "DELETE", params: { TestId: 0 } }
});
var factory = {
query: function (selectedSubject) {
var deferred = $q.defer();
TestResource.query({ subjectId: selectedSubject },
function (resp) {
deferred.resolve(resp);
}
);
return deferred.promise;
//return Test.query({ subjectId: selectedSubject });
}
}
return factory;
}]);
In my controller I am calling it this way:
TestService.query($scope.selectedSubject)
.then(function (result) {
$scope.gridData = result;
}, function (result) {
alert("Error: No data returned");
});
Is there a way that I could cut out a few lines of code by not having $q in my service. Is there a way that I could return a promise from the $resource? I have tried the commented out code but that gives me an error saying there is no ".then" method.
$resource can't return a promise in the current stable version of Angular (1.0.8), but it looks like it'll come in version 1.2. It was added in v1.1.3 (v 1.1 is the unstable branch):
$resource: expose promise based api via $then and $resolved (dba6bc73)