AngularJS $httpBackend - "No more request expected" error - angularjs

It seems this is working solution that shows how to work with $httpBacked http://jsfiddle.net/EgMpe/8/
But for my case:
routes
app.config(['$routeProvider', function($routeProvider) { $routeProvider.
when('/', {templateUrl: 'partials/user-list.html'}).
...
faked service:
app.run(function($httpBackend) {
var users = [{"id":1,"name":"bob","email":"bob#bobs.com"}, {"id":2,"name":"bob2","email":"bob2#bobs.com"}]
$httpBackend.whenGET('/rest/users').respond(function(method,url,data) {
console.log("Getting users");
return [200, users, {}];
});
});
..
real service:
services.factory('Users', function($resource){
return $resource('/rest/users', {}, {
get: {method: 'GET', isArray:true}
});
});
I have error when go to my "/" route that redirects me to user-list.html page:
Error: Unexpected request: GET partials/user-list.html No more request
expected
at $httpBackend .../mysite/public/angular/libs/angular-1.2.0/angular-mocks.js:1060:9)
Question1: Does httpBackend prevent doing any other http request?
I tried to use passThrough method to let http hit real server side:
$httpBackend.whenGET(/^\/mysite\//).passThrough();
But this does not help.

Using $httpBackend you have to specify in advance all request you are going to perform. Maybe this short excerpt from Mastering Web Application Development with AngularJS will clarify why:
The verifyNoOutstandingExpectation method verifies that all the expected
calls were made ($http methods invoked and responses flushed), while the
verifyNoOutstandingRequest call makes sure that code under test didn't trigger
any unexpected XHR calls. Using those two methods we can make sure that the code
under the test invokes all the expected methods and only the expected ones.

Ah.. Sorry I just was wrong with my RegEx:
if type this $httpBackend.whenGET(/partials/).passThrough();
Then all start working.
So, I got my lesson: don't forget to put: passThrough(); with right RegEx.

Related

Trouble understanding how angularjs $httpDefaultCache, $cacheFactory and how to access that data

I'm having to inline all resources into one file and this includes all the data that my application uses. With a gulp process, I've been able to create a $cacheFactory with all the data:
angular.module('app').run('$cacheFactory', '$http', function($cacheFactory, $http){
var $httpDefaultCache = $cacheFactory.get('$http');
$httpDefaultCache.put('/data/names.json',{...});
$httpDefaultCache.put('/data/places.json',{...});
});
My understanding of how to access this instead of making a call externally (file) may be incorrect.
I thought that by setting $httpProvider.defaults.cache = true, that my request to the endpoints of above would use the default cache.
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.cache = true;
}]);
Instead I get an error of
https://.../data/names.json 404 (Not Found)
As if it is looking within the the client and not in angular's cache. The type is also an xhr.
data.load(names, '/data/names.json');
...
function load(broadcastName, url, force){
if(!isLoaded[broadcastName] && !force){
console.log('Loading name data from', url);
// Make the http GET request
http({
method: 'GET',
url: url,
cache: true
}).then(function success(response){
console.log("response: ", response)
...
})
Had to create a custom http request that perfectly imitates angular $http service. It is used in several other applications we have, so I know it works. Only thing that has been added for this implenation is cache:true.
I've looked at several other questions that were similar, but I am still not understanding how it is working. How does using http default cache work? And is there something I should be aware of that I may be glossing over?
I hope this makes sense.
There's no special term for $http cache like '$httpDefaultCache'. It works like you expect it to work. You set either $httpProvider.defaults.cache or cache request option to true, and the response is retrieved from $http cache, which is available by default as $cacheFactory.get('$http').
Cache is just key/value storage. If request URL doesn't match a key completely, cache won't be used.
Here's an example how it works:
$cacheFactory.get('$http').put('/foo', 'foo');
$http({ url: '/foo', cache: true })
.then(result => {
$scope.foo = result.data;
})
.catch(result => console.error(result))

Getting error when trying get JSON from remote url

I'm trying load this json file from remote url. In the beginning I was using $http.get function, but I was getting the next error message:
CORS 'Access-Control-Allow-Origin'
Now I am using JSONP, but nothing happens.
service.js file:
angular.module("elcomaApp").factory('ElcomaService', ['$http', function($http){
return $http({
method: 'JSONP',
url: 'http://vagalumewifi.com.br/timeline.json'
}).success(function(response){
return response.data;
}).error(function(err){
return err;
});
}]);
controller.js file:
angular.module("elcomaApp", []).controller('MainController', ['$scope', 'ElcomaService', function($scope, ElcomaService){
$scope.name = 'Natanael Santos';
console.log($scope.name);
ElcomaService.success(function(data){
$scope.elcomaData = JSON.parse(data);
var i = 0;
for (x in $scope.elcomaData){
console.log(i);
i++;
console.log(x.date);
}
}).error(function(data){
console.log(data);
});
}]);
app.js file:
var app = angular.module("elcomaApp", ['ngMaterial', 'ngRoute']);
I already hava read a lot of articles on stackoverflow, but no one work for me.
I'd suggest using $http.jsonp(url) method:
angular.module("elcomaApp").factory('ElcomaService', ['$http', function($http) {
$http.jsonp('http://vagalumewifi.com.br/timeline.json')
.success(function(data) {
console.log(data); // you can't `return` here...
}).error(function(err){
console.err(err);
});
}]);
Note: be warned that you can't expect that return in an async method has the same behavior as in a sync environment... :-)
Your original error is your clue. The endpoint server won't allow access from another domain.
CORS: Cross Origin Requests
You need to allow access on the endpoint server for the type of HTTP method you want to use (i.e. GET, POST, HEAD, ...) Additionally depending on what you're doing you may need to allow for an OPTIONS request, see Preflighted Requests in the MDN documentation above.
If you don't have access to that server you may need to do a work around by making $http call a script on your server that will fetch the file for you. I've done this before using PHP as a proxy and using PHP's file_get_contents function to grab files from other servers of a different domain.

Angular mock $httpBackend give No pending request to flush

Following the official guide at angularJS $httpBackend I'll do this test, but Karma give me this error:
Error: No pending request to flush !
at Function.$httpBackend.flush
Test
'use strict';
describe('profileCtrl', function () {
var scope, $httpBackend;
beforeEach(angular.mock.module('maap'));
beforeEach(angular.mock.inject(function($rootScope, $controller, _$httpBackend_){
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', 'profile').respond([{id: 1, name: 'Bob'}]);
scope = $rootScope.$new();
$controller('profileCtrl', {$scope: scope});
}))
it('should fetch list of users', function(){
$httpBackend.flush();
expectGET(scope.current_user.length).toBe(1);
expect(scope.current_user[0].name).toBe('Bob');
});
});
for this simple controller:
'use strict';
angular.module('maap').controller('profileCtrl', function($scope, UserService) {
$scope.current_user = UserService.details(0);
});
The _$httpBackend_ has nothing to flush because you don't make any http request in your test.
You need to invoke some code that make an http request.
Then, once something somewhere made an http request in your test, you can call the flush method so that a response is provided for the request that has been made.
Something like:
it('should fetch list of users', function(){
// Do something that will make an http request
MyService.getAllUser(function ...) // for example
// Then provide a response for the http request that
// has been made when getAllUser was called
$httpBackend.flush();
// Check the expected result.
expect(something).toBe('Bob');
});
Same issue happened to me and the problem was not that I was not making a request but because the request I was making was different to the expected one:
For example I have defined this expectation:
mockBackend.expectGET("http://myapi.com/001").respond("RESULT");
And I was requesting this other URL:
http://myapi.com/002
Very confusing error message, no really easily related with the underneath problem.
I had the same issue, because I neglected to define a response for each expected request. With no response, there becomes nothing to flush. Also the http promise would never resolve or fail.
I've got the same exception because I used ngMockE2E module instead of ngMock module. Even calling $rootScope.$digest() didn't help.
Maybe you are not using $http AngularJs service in your ajax call.
I happened to me where I expected $httpBackend to flush something, but I got an error:
Error: No pending request to flush !
so after a long investigation I found out that the ajax was written in jQuery instead of $http
So just make sure that you use $http service for your Ajax calls.

How to load data synchronously in AngularJS application

Now I know that because of the way javascript executes it is recommended that you run all remote requests as async instead of sync. While I agree with that 99% of the time, sometimes you do want to run remote request as a sync instead of a async. For example, loading session data is something I would want to do synchronically as I don't want any views to render until that data is loaded. This plunker shows the issue with loading session data asynchronically (NOTE: I am using $timeout to simulate what would happen with an async call):
http://plnkr.co/edit/bzE1XP23MkE5YKWxRYrn?p=preview
The data property does not load anything because the data is not available when it tries to get it and data2 does only because the data is available when it tries to get it. Now in this case I could just put the session variable on the scope and be done with it but that is not always the case.
Is there a better way to do sync remote calls in an angular application other than using jQuery's .ajax() method (trying to depend on jQuery as little as possible)?
If you want the session data to be loaded prior to a controller being loaded, you should included it as as resolve parameter (assuming you are using the $routeProvider).
For example:
angular.module('mymodule', ['ngResource'])
/* here's our session resource. we can call Session.get() to retrieve it. */
.factory('Session', ['$resource', function($resource) {
return $resource('/api/session.json');
}])
/* here's our controller + route definition. */
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/foo', {
controller: 'MyCtrl',
templateUrl: '/templates/foo.html',
/* the controller will not be loaded until the items
* below are all resolved! */
resolve: {
session: ['$q', 'Session', function($q, Session) {
var d = $q.defer();
Session.get(function(session) {
/* session returned successfully */
d.resolve(session);
}, function(err) {
/* session failed to load */
d.reject(err);
});
return d.promise;
}]
}
});
}])
.controller('MyCtrl', ['$scope', 'session', function($scope, session) {
/* 'session' here is the key we passed to resolve above.
* It will already be loaded and resolved before this function is called */
$scope.session = session;
}]);
Angular is hardcoded to make the requests async. To do it synchronously would take other code, whether custom or from some other library. Here is line 9269 from angular 1.0.7:
xhr.open(method, url, true);
The third param makes it asynchronous.
I would take a step back and think about how you are doing things. You could provide some loading indicator while your async request is going and easily control the loading of a view in the success callback so that it doesn't appear until the data is loaded.
A better solution is to add a response interceptor:
checkAuth = ($q, $location) ->
success = (response) ->
response
error = (response) ->
errorCode = response.status
$location.path '/login' if errorCode is 403 or errorCode is 401
# $q.reject response - no need because we are redirecting before any other promises in the chain will resolve (were breaking our future promises)
(promise) ->
promise.then success, error
$httpProvider.responseInterceptors.push checkAuth
And in your $routeProvider, or $stateProvider in my case:
.state 'user',
templateUrl: 'assets/views/user/partials/user.html'
resolve:
credentials: (checkLogIn) ->
checkLogIn.get().$promise
When checkLogIn.get()'s promise is rejected (the error handler is fired), assuming it's a 401 or 403 response (unauthenticated or unauthorized), the promise chain will be broken and the user will be "redirected" to /login.
With this method, any error calls will be channelled into the interceptor, instead of handling errors on a route-by-route basis.

angular.js and untappd API, how to send a Get request

I'm trying to connect to Untappd API trought angular.js; the API docs says
Whenever you are making a call to the API, you MUST pass both your Client ID and Client Secret as GET params like below
http://api.untappd.com/v4/method_name?client_id=CLIENTID&client_secret=CLIENTSECRET
with angular I have done this simply controller
function UntappdController($scope,$http) {
$http.get('http://api.untappd.com/v4/user/badges/jonnyjava?client_id=XXX&client_secret=XXX').success(function(data) {
alert('ok');
}).
error(function(data, status, headers, config) {
alert('ko');
});
}
UntappdController.$inject = ['$scope', '$http'];
but it doesn't work. (I get always KO)
So I'have tried tro a RESTful service. In this way
angular.module('BadgeServices', ['ngResource']).
factory('Badge', function($resource){
return $resource('http://api.untappd.com/v4/user/badges/jonnyjava/', {}, {
query: {method:'GET',params:{client_id: 'XXX', client_secret: 'XXX'}, isArray:true}
});
});
But this doesn't works too...
What I'm doing wrong? I'm new to angular. It looks simple but I'm missing something fundamental...
The error function is passed these variables: data, status, headers, config. Check their contents - I'm sure the server has some why of specifying what went wrong.
Sorry, I forgot it! Here is what I get with the error function..
data: status:0 headers:function (name) {
"use strict";
if (!headersObj) headersObj = parseHeaders(headers);
if (name) {
return headersObj[lowercase(name)] || null;
}
return headersObj;
}config:[object Object]
Nothing helpful...
Solved! It was a CORS problem. If anyone is interest the solutions is here.
stackoverflow-Angularjs issue $http.get not working

Resources