AngularJS - Using ngMockE2E $httpBackend how can I delay a specific response? - javascript

I'd like to delay the response to the following whenGET:
$httpBackend.whenGET(/^foobar/).respond(function () {
return [200,{}];
});
However it seems impossible using $timeout to do this synchronously, so I'm not sure how to approach this?

If you want to delay only a specific response, than you may delay assignment of the reponse to scope property.
If you wrap your call into your custom service method, than you may wrap the response into the promise and resolve it when needed:
JS:
angular.module('app').service('myService', function($q, $timeout){
this.getData = function() {
var delay = 300,
origPromise = $http.get('someUrl'),
deferred = $q.defer();
$timeout(function() {
deferred.resolve(origPromise);
}, delay);
return defered.promise;
};
});
[EDIT]:
To set the delay to all requests, you may apply this solution to the response interceptor

Related

Get variable value outside of Promise [duplicate]

This question already has answers here:
How can I access a variable outside a promise `.then` method?
(2 answers)
Closed 5 years ago.
I have a service in AngularJs which will return a value from Database.
userData.getUserData(function(response) {
$scope.uid = response
});
when I inject this service in my controller it will return a Promise,
But i need this Promise value Outside of my Function, how can i do that ?
Plunkr link of code
From your plunker code you have a service which looks like this:
angular.module('plunker');
.service('myService', function($firebaseRef, $firebaseObject){
this.getUserData = function(el) {
$firebaseObject($firebaseRef.users.child(this.localStorage().uid)).$loaded(function(data) {
el(data);
})
}
});
and a controller like this:
app.controller('MainCtrl', function($scope, myService) {
myService.getUserData(function(response) {
$scope.uid = response;
})
console.log($scope.uid);
$scope.postRequest = function(val) {
$firebaseArray($firebaseRef.requests.child($scope.uid)).$add(val);
console.log(val)
console.log($scope.request);
}
});
The problem is that the line console.log($scope.uid); prints undefined.
You are thinking in the terms of a standard blocking programming, but in this case the call to getUserData is non-blocking which means that you don't wait for the response, instead you only send the request to the server (Firebase) and continue to the next statement which is console.log.
The callback function(response) { $scope.uid = response; } will be invoked when the client reads success response (HTTP 2xx) returned by the server. This takes at least the time request travels to the server and response to travel back + the time it takes for the server to actually get the data. For example 150ms.
So, basically at the time the console.log statement is executed, the response callback was still not invoked, ie. the $scope.uid is not set which means that the console.log will print undefined.
To resolve this you need to execute your code, which depends on the response from the server, in the callback itself. For example something like this:
app.controller('MainCtrl', function($scope, myService) {
myService.getUserData(function(response) {
$scope.uid = response;
console.log($scope.uid);
// and any other code which depends on the $scope.uid
});
// ...
});
The cool factor would be to use AngularJS promises via $q service. For example, you could redefine your service like this:
angular.module('plunker');
.service('myService', function($q, $firebaseRef, $firebaseObject){
var deferred = $q.defer();
this.getUserData = function(el) {
$firebaseObject($firebaseRef.users.child(this.localStorage().uid)).$loaded(function(data) {
deferred.resolve(data);
});
};
return deferred.promise;
});
then in your controller you can use your service method like this:
app.controller('MainCtrl', function($scope, myService) {
myService.getUserData()
.then(function(data) {
$scope.uid = data;
console.log($scope.uid);
// and any other code
// you can also return promises here and then chain
// them, read about AngularJS promises
});
// ...
});
This is basically same as the example before, but with added benefit of better readability which is accomplished by avoiding callback hell.
I noticed that you have postRequest function which uses $scope.uid. I guess that you do not want to execute this function if you do not have $scope.uid. I also guess that this function is called by some event, like click on a button. My recommendation is that you disable the button or whatever else invokes this function until the $scope.uid is loaded.
For example like this:
<button type="button" ng-click="postRequest(something)" ng-disabled="uid === undefined">Post</button>
Hope this helps.
You issue that has been discussed has to do with the fact that you are trying to use $scope.uid before your promise has returned anything.
You can get around things like this by taking a few steps, mainly, you can init the scope var before you use. For instance if the response is an object you could just do something like this:
$scope.uid = {};
userData.getUserData(function(response) {
$scope.uid = response;
});
Then your var wont be undefined. But you should also consider when and how you are using this variable, that will effect if you want to init like this or not.
If you log like this, it will work
userData.getUserData(function(response) {
$scope.uid = response;
console.log($scope.uid);
});
and if you log like this it will not work because this log is not going to wait for you promise to return before logging;
userData.getUserData(function(response) {
$scope.uid = response;
});
console.log($scope.uid);
You'd need to provide more information to determine how best to deal with using this returned information and local variable. But the general idea of the problem is that you are attempting to log the variable before the promise is back.
TL:DR You have access to $scope.uid outside of the function, you need to wait for the reponse to give it the data before it will be inside, you can init it if you do not want it to start out as undefined
UPDATE : you need to use a callback to fire the second call After you have the first call back
userData.getUserData(function(response) {
$scope.postRequest(response);
});
$scope.postRequest = function(val) {
$firebaseArray($firebaseRef.requests.child($scope.uid)).$add(val);
console.log(val) console.log($scope.request);
}
Your plunk fixed : https://plnkr.co/edit/KbVoni3jfnHm54M80kYl?p=preview
You have to wait until the process of getting the response from userData.getUserData is done.
There are 3 ways as far as I know to solve this:
Using Callback
function getUserData(callback){
userData.getUserData(function(response) {
callback(response);
});
}
then you call that function
getUserData(function(response){
$scope.uid = response;
// then you can proceed and use the $scope.uid here
});
Wrap it in function
getUserData(function(response){
callAnotherFunction(response);
});
function callAnotherFunction(response){
console.log(response);
// You can use the value inside this function
}
Or use timeout
You can use $timeout to give time to the request and assign it to $scope.uid

how to call a factory from a regular function in AngularJs

I have this factory which is called by some controllers.
app.factory('fileData', function($http) {
return {
get: function(filename){ return $http({ method: 'GET', url: filename});
}
};
});
Now I want to call it from a regular function and return the data from the factory. How can I do that? This one does not work because the fileData passed in is not recognized without $scope.
function getData (file, fileData) {
rels = [];
var handleSuccess = function(data, status) {
rels = data;
console.log(rels);
};
fileData.get(filename).success(handleSuccess);
return rels;
}
Any idea how to go around this?
Move return rels inside handleSuccess, you're returning before handleSuccess is getting called. So you're signaling that the function completed execution prematurely.
function getData (file, fileData) {
rels = [];
var handleSuccess = function(data, status) {
rels = data;
console.log(rels);
return rels; // Return rels when the Promise is finished
};
fileData.get(file).success(handleSuccess);
}
Since you're returning the promise from $http, you need to handle its execution in an asynchronous way which, is simply not returning until the promise is resolved.
It would also be a good idea to add a reject handler.
Additionally, .success() is deprecated and you should use .then() and .catch() respectively for handling resolve() and reject() of the Promise appropriately.
See below for the preferred approach to Promises with $http
fileData.get(file)
.then(handleSuccess)
.catch(handleFailure); // Handle any errors returned from $http
EDIT Update for DI example for controller
You need to inject the fileData factory into your Angular Controller. For more info on Dependency Inject see the Angular Docs for DI. Just to note, DI is a huge piece of Angular so it is pretty fundamental to understand this before proceeding.
angular
.module('yourApp')
.controller('yourController', ['$scope', 'fileData', function($scope, fileData) {
// Expose getData via $scope
$scope.getData = getData;
function getData(file, fileData) {
var rels = [];
var handleSuccess = function(data, status) {
rels = data;
console.log(rels);
return rels;
};
fileData.get(file)
.then(handleSuccess);
}
}]);
Your call to the method exposed in the factory (get) uses the $http service which returns a promise. So you cannot get the result of this call in a synchronous way.
In your sample the return rels happens way before the handleSuccess method is called and thus you are returning the old value (an empty array).
The reason it works inside of a controller function is that the controller instance is living for a longer time and when the server call returns successfully, the variable in you controller's $scope is assigned (which then is reflected in your UI via the angular data binding).
So the best way to solve this, is to avoid having a synchronous api in your "regular function". Could you probably make this also async by returning a promise?

Cancel / Abort all pending requests in angularJs

On route change, I need to abort ALL pending requests from previous route so that I don't run into problems of responses from previous route messing up data on my current route (it happens sometimes when responses from previous route take long time to finish).
I have thought about using http interceptor for this:
$httpProvider.interceptors.push(function($q) {
return {
'request': function(config) {
},
'response': function(response) {
}
};
});
In the request function, I could modify the config.timeout with a promise as suggested here and store all the deferred objects in a global cache so that I could cancel all of them.
The problem with this approach is that it may override config.timeout set in other places in the code.
I think another solution could be to cancel all ajax requests at XMLHttpRequest level, but I don't know how to do it.
Any suggestions? Thanks.
As you say, timeout is the only API we have of use right now to cancel a running $http request. I think you're right on the money with an interceptor coupled with a cancel promise.
What you could do is attach the full deferred object on the $http request, and cancel all pendingRequests in your route change handler.
Something like this could (perhaps*) work?
angular.module('module').config(function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
request: function (config) {
if (!config.timeout) {
config.cancel = $q.defer();
config.timeout = config.cancel.promise;
}
return config;
}
}
});
});
angular.module('module').run(function ($rootScope, $http) {
$rootScope.$on('$stateChangeStart', function () {
$http.pendingRequests.forEach(function (pendingReq) {
if (pendingReq.cancel) {
pendingReq.cancel.resolve('Cancel!');
}
});
});
});
*: I say perhaps, because I had success with this approach, but it's seldom you find a silver bullet to something like this.
edit
If you need to bypass the error handler of the cancelled promise, hook into responseError property and do manual labour there.
angular.module('module').config(function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
responseError: function (response) {
if (response.config.timeout.$$state.value === 'Cancel!') {
// debugger;
return $q.when('bypassed');
}
}
}
});
});
I'm starting to think that there is no generic/'cool' solution to reach the end result you desire. Treading some odd ground here : )
edit2:
Testing the above myself now. Returning $q.when('something') in the responseError will effectively bypass the error callback of the cancelled $http request. Let me know if it works out for you.

How to cancel an ongoing REST-call using Angular's $resource?

I've been looking over and over for an example on how to cancel an ongoing REST-call using Angular's $resource. I haven't found any solution yet, but from the Angular documentation I got the impression that it should be possible.
From the documentation:
Usage:
$resource(url[, paramDefaults][, actions]);
One of the actions defined in the documentation:
timeout – {number|Promise} – timeout in milliseconds, or promise that should abort the request when resolved.
Does anyone have a working example showing how to use this timeout action with promise to cancel an ongoing request? Is it possible?
My code example:
var canceler = {};
$scope.doSomething = function() {
canceler = $q.defer();
$http.post('url', data, {timeout: canceler.promise}).
success(function(data) {
}).
error(function() {
});
};
function cancelPost() {
canceler.resolve(); //aborts request
}
}
Yes this is possible. You have to create a defere and set the promise as parameter:
var timeoutPromise = $q.defer();
{timeout: timeoutPromise.promise}
Then you can resolve the promise at any time:
timeoutPromise.resolve();
It should also be possible to call $timeout.cancel(timeoutPromise). What should be equal to timeoutPromise.reject().
$timeout $q

Unit testing an asynchronous service in angularjs

I am trying to unit test a service which has asynchronous methods but am having no luck.
I have tried to implement with promises by using the $q support in angularjs.
Any help would be appreciated.
http://jsfiddle.net/9pBze/37/
angular.module('myapp', ['myservice']);
angular.module('myservice', []).factory('myservice', function($q) {
var ls = {};
ls.DoIt = function() {
var deferred = $q.defer();
setTimeout(function(){
deferred.resolve(5);
},3000);
return deferred.promise;
}
return ls;
});
describe('services', function () {
beforeEach(module('myservice'));
it("should equal 2", inject(function(myservice) {
myservice.DoIt().then(function(returned) {
expect(returned).toEqual(2);
});
}));
});
First of all, the setTimeout is particularly tricky to test since it hard to mock. Fortunately AngularJS has a wrapper around it ($timeout) that plays the same role but can be easily mocked:
ls.DoIt = function() {
var deferred = $q.defer();
$timeout(function(){
deferred.resolve(5);
},3000);
return deferred.promise;
}
The mock provided for $timeout allows us to easily simulate elapsed time (with $timeout.flush()) which means our tests can run fast, without really waiting for the async event to complete (please note that the production code is still using async API!).
The changed tests would look like:
it("should equal 5", inject(function(myservice, $timeout) {
var valueToVerify;
myservice.DoIt().then(function(returned) {
valueToVerify = returned;
});
$timeout.flush();
expect(valueToVerify).toEqual(5);
}));
And finally the working jsFiddle: http://jsfiddle.net/v9L9G/1/
It's not related to Angular itself, but to Jasmine async tests.
If you need a setTimeout use Angular $timeout. And if you wish to have a fine control over setTimeout/$timeout executions, use mocked Clock.

Categories

Resources