Intercept $http request and return hardcoded data - angularjs

I would like to intercept all $http calls made by various services and return an object which is declared inside the interceptor a.k.a. hardcoded data.
The request interceptor provided by Angular seems to only be able to change and return the HTTP config object.
How can I manipulate the data returned without actually calling a server?
Thanks.

To use $httpBackend from MockE2E to provide mock API responses, either for testing purposes, or to provide a mock API to develop the client against if the server API is not available, firstly you need to include angular-mocks.js as this contains ngMockE2E.
Then you need to add a module something like:
angular.module('mockBackend', [ 'ngMockE2E'])
.run(function($httpBackend) {
phones = [{name: 'phone1'}, {name: 'phone2'}];
// returns the current list of phones
$httpBackend.whenGET('/phones').respond(phones);
// adds a new phone to the phones array
$httpBackend.whenPOST('/phones').respond(function(method, url, data) {
var phone = angular.fromJson(data);
phones.push(phone);
return [200, phone, {}];
});
$httpBackend.whenGET(/^\/templates\//).passThrough();
//...
});
The above is taken from the docs at https://docs.angularjs.org/api/ngMockE2E/service/$httpBackend - you would need to set up in here all your API endpoints that you want to return mock data for, along with any that you want real data for, eg. in the above any requests for 'templates' uses .passThrough to still forward these requests to the server, but returns mock data for API calls to '/phones'.
As far as how to turn this off and on goes it will depend on how you have your angular build process set up. If you do not want any of this in your production version you could put the above in a separate mockbackend.js file and add
angular.module('myApp').requires.push('mockBackend');
to the bottom of the file - where 'myApp' would be your app module. For your production files you can then have your build process (manual or automated) remove the angular-mocks.js and mockbackend.js requires from your index.html file and all your api calls will revert to calling the server.
If you wanted to vary if the mock backend is used or not during development you could pass a constant into the mockBackend module and use this to decided if real or mock data is returned, eg. if you have a constant 'DEV' which is a simple boolean:
if (DEV === true) {
$httpBackend.whenGET('/phones').respond(phones);
} else {
$httpBackend.whenGET(/phones).passThrough();
}

Related

GET call failing in AngularJS resource - Strange data result

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.

Modify or access angular interceptors after config phase

Is it possible to access / modify $http interceptors after the config phase? I'm debugging an app that only breaks in production due to being deployed on a different server, so unfortunately I can't change the interceptor code locally and figure out what's going on.
If it's not possible to access / modify the interceptors, perhaps it'd be possible to replace $http. Here's an example of replacing a hypothetical service:
var inj = angular.element('body').injector(),
oldGet = inj.get,
mockService = { secret: 'shhh' };
inj.get = function(str) {
if (str === 'some-service') {
return mockService;
} else {
return oldGet.apply(inj, arguments);
}
};
However, I'm not sure how I'd go about creating a new $http service (into which I could pass in the modified interceptors). I can't grab the $httpProvider, either.
Perhaps bootstrapping a new ng-app on a separate part of the page would work? Then I could grab the $http service and replace it, like above.
Other ideas:
With reference to: Right way to disable/remove http interceptors in Angular? , it does not seem like I can access the interceptors array if I don't hold on to it in the config phase.
Perhaps I can use grease monkey to inject something that runs in the config phase.
Thank you!

Is it suitable to use $httpBackend in production to abstract data service requests?

I have a data service in my application that is responsible for retrieving information for my controllers. This information might come from local storage, window or an ajax request. The problem I am facing is the $q promise responses don't look like $http responses.
this.getContactDetails = function(data) {
// The first time this method is called, we expect contact details to be preloaded on the page.
// We want to read and return that object then remove it from the page so subsequent requests are to the server.
if(typeof $window.preloadData.contact !== 'undefined') {
var contactDetails = JSON.parse(JSON.stringify($window.preloadData.contact));
delete $window.preloadData.contact;
// Since the method call should always have the same return type, we manually create a deferred object and set the resolution using the $q service.
var deferred = $q.defer();
deferred.resolve(contactDetails);
return deferred.promise;
}
var request = requests.contactDetails.get;
return $http(request);
};
The $q service does a nice job here but it resolves as the object it was given. I wouldn't really expect it to wrap the response. I know $httpBackend could accomplish this.
$httpBackend.whenGET(request).respond(contactDetails);
But the service is used in the MockE2E library and I doubt this was its intended use. I am not sure how to call this off afterwards or what would happen if I used it twice on the same request but I can figure out these questions. My other concern is that there doesn't seem to be a way to pass the same config object to $httpBackend as I do to $http. $httpBackend only accepts a method, url, body and headers, while $http config allows me to specify parameters.
Currently my work-around is simply to create and $http-like wrapper myself.
var contactDetails = JSON.parse(JSON.stringify({
data: $window.preloadData.contact
}));
But I don't find this very elegant. Is there a better/correct way to do this?
You can implement your storage layer as a $cacheFactory and add it to $httpProvider during the configuration phase.
From the docs:
When the cache is enabled, $http stores the response from the server in the specified cache. The next time the same request is made, the response is served from the cache without sending a request to the server.
Hence, if you provide your own implementation of a cache with the following methods:
{object} info() — Returns id, size, and options of cache.
{{*}} put({string} key, {*} value) — Puts a new key-value pair into the cache and returns it.
{{*}} get({string} key) — Returns cached value for key or undefined for cache miss.
{void} remove({string} key) — Removes a key-value pair from the cache.
{void} removeAll() — Removes all cached values.
{void} destroy() — Removes references to this cache from $cacheFactory.
You can return values read from localStorage, session cookies, etc. and they will be treated as there were data sent from the server, just without the AJAX request.

Angular factory with parameters

I have a small factory which requests data from a database (through php pages which return json objects). However to do this I need to set certain parameters in the get request. I have created a factory object to make the request.
app.factory('getplayerfactory', function($http){
return{
getPlayer: function(callback, name, currentinnings) {
var file = "/ajax.php?file=getplayer&displayname="+name+"&currentinnings="+currentinnings
$http.get(file).success(callback)
}
}
})// end of getplayersfactory
(I am using npm coding standard so no semi colons at the end of lines)
In my controller I want to call this factory and then use the results to fill data. I have tried to use the following to call this
getplayerfactory.getPlayer(function(results, "M. Millent", 1){
$scope.players[0].setHowout(results.howout)
})
However this creates an error when I introduce more parameters than results. I have used this factory pattern with other $http data request where the get request does not need parameters which works fine.
How do I make a get request which sets parameters? or do I need to create a separate factory for each set of parameters?
function(results, "M. Millent", 1) { is not a valid function signature
I think this is what you meant:
getplayerfactory.getPlayer(function(results){
$scope.players[0].setHowout(results.howout)
}, "M. Millent", 1)

Partial Updates (aka PATCH) using a $resource based service?

We're building a web application using Django/TastyPie as the back-end REST service provider, and building an AngularJS based front end, using lots of $resource based services to CRUD objects on the server. Everything is working great so far!
But, we would like to reduce the amount of data that we're shipping around when we want to update only one or two changed fields on an object.
TastyPie supports this using the HTTP PATCH method. We have defined a .diff() method on our objects, so we can determine which fields we want to send when we do an update. I just can't find any documentation on how to define/implement the method on the instance object returned by $resource to do what we want.
What we want to do is add another method to the object instances, (as described in the Angular.js documentation here) like myobject.$partialupdate() which would:
Call our .diff() function to determine which fields to send, and then
Use an HTTP PATCH request to send only those fields to the server.
So far, I can't find any documentation (or other SO posts) describing how to do this, but would really appreciate any suggestions that anyone might have.
thank you.
I would suggest using
update: {
method: 'PATCH',
transformRequest: dropUnchangedFields
}
where
var dropUnchangedFields = function(data, headerGetter) {
/* compute from data using your .diff method by */
var unchangedFields = [ 'name', 'street' ];
/* delete unchanged fields from data using a for loop */
delete data['name'] ;
delete data['street'];
return data;
}
PS: not sure from memory, whether data is a reference to your resource of a copy of it, so you may need to create a copy of data, before deleting fields
Also, instead of return data, you may need return JSON.stringify(data).
Source (search for "transformRequest" on the documentation page)
We implemented $patchusing ngResource, but it's a bit involved (we use Django Rest Framework on the server-side). For your diff component, I'll leave to your own implementation. We use a pristine cache to track changes of resources, so I can poll a given object and see what (if any) has changed.
I leverage underscore's _.pick() method to pull the known fields to save off the existing instance, create a copy (along with the known primary key) and save that using $patch.
We also use some utility classes to extend the built-in resources.
app.factory 'PartUpdateMixin', ['$q', '_', ($q, _) ->
PartUpdateMixin = (klass) ->
partial_update: (keys...) ->
deferred = $q.defer()
params = _.pick(#, 'id', keys...)
o = new klass(params)
o.$patch(deferred.resolve, deferred.reject)
return deferred.promise
]
Here's the utility classes to enhance the Resources.
app.factory 'extend', ->
extend = (obj, mixins...) ->
for mixin in mixins
obj[name] = method for name, method of mixin
obj
app.factory 'include', ['extend', (extend) ->
include = (klass, mixins...) ->
extend klass.prototype, mixins...
return include
]
Finally, we can enhance our Resource
include TheResource, PartUpdateMixin(TheResource)
resourceInstance = TheResource.get(id: 1234)
# Later...
updatedFields = getChangedFields(resourceInstance)
resourceInstance.partial_update(updatedFields...)
I would suggest using Restangular over ngResource. The angular team keeps improving ngResource with every version, but Restangular still does a lot more, including allowing actions like PATCH that ngResource doesn't. Here'a a great SO question comparing the two What is the advantage of using Restangular over ngResource?

Resources