Testing for whether an object is an angularJS $resource - angularjs

Simple (seeming) question - I'm trying to do a simple sanity check in my AngularJS controller to make sure that my $resource is actually instantiated as such. It's a largish app, but for example:
.factory('AccountSearchService_XHR', ["$resource", function($resource) {
var baseUrl = "http://localhost\\:8081/api/:version/accounts/:accountNumber";
return $resource(baseUrl,
{
version: "#version",
accountNumber: "#accountNumber"
},
{
get: {method: 'GET', isArray: false}
});
}]);
Then later, in controller:
$scope.accountObj.currentAccount = AccountSearchService_XHR.get({
version: "v1",
accountNumber: "1234"
},
function(result) {... etc etc});
The call to my API works fine, everything returns data like I expect - but I'd like to test to see if $scope.accountObj.currentAccount is a Resource before trying to make the .get call (notice the super important capital "R").
When I inspect the object $scope.accountObj.currentAccount in chrome debugger, it looks like:
Resource {accountHolderName: Object, socialSecurityNumer: null, birthDate: "05/14/1965", maritalStatus: ...}
Because of some complexity in my setup though, occasionally it gets overwritten as a normal object (typeof returns "object"), but inspecting it in debugger confirms it lost its Resource status.
So - does anyone know of a way to test whether it is a $resource? Almost like typeof $scope.accountObj.currentAccount returns "Resource"? Or perhaps a better best practices way to ensure that things are connecting up all proper and respectable-like?
All the SO articles I have seen when searching revolve around actual Jasmine testing.
Thanks in advance.

#tengen you need to have injected the type you want to check against, instead of $resource.
All resources are instances of the "class" "Resource", but that's a function that's defined inside of the factory method of the $resource service, so you have no outside visibility to use it with the instanceof operator.
However, you're wrapping that $resource creating with your own custom type, AccountSearchService_XHR, and that's what you need to make the check against.
You need AccountSearchService_XHR to be injected in your code and then perform myRef instanceof AccountSearchService_XHR and that will be === true.

Digging up an old question my intern just had. The simple solution is:
if ($scope.accountObj.currentAccount instanceof AccountSearchService_XHR)
return 'This is a AccountSearchService_XHR Resource';
else
return 'This is not a AccountSearchService_XHR Resource';
which with proper names (Users being a $resource) and real case scenario should lead you to write something like this:
if (!(this.user instanceof Users))
this.user = new Users(this.user);
this.user.$update();

Check it via instanceof yourVariable === "Resource". Because Resource is an object the type will always return as an Object, but if you check that it's an instance of the Resource "class" that should work just fine.

Related

AngularJS Unit Testing: Attaching Data from $q.resolve() to object

I'm testing a service that uses another service for API calls, let's call this the data service. The data service is tested elsewhere, so I've abstracted it away with a simple implementation that contains empty functions; I'm returning data via a deferred object and Jasmine's spyOn syntax.
The trouble I'm finding with this approach is when the data is returned, it's not immediately available on the calling object, as it would be if I used $httpBackend. Aware I could just use $httpBackend, but I'd like to know if I've missed something (simple or otherwise) in this approach.
Example section of code I'm trying to test:
storeTheData = dataService.getSomeData();
storeTheData.$promise.then(function(data) {
/*this would work*/
console.log(data);
/*but this would not, when testing using $q*/
_.forEach(storeTheData, function(storedData) {
/*do something with each object returned*/
});
});
As a side note, I don't think the situation is helped by the ...$promise.then on another line, but ideally I wouldn't change the code (I'm providing test coverage to something written a while ago...)
Example of the test:
beforeEach(
...
dataService = {
getSomeData: function () { }
};
getSomeDataDeferred = $q.defer();
spyOn(dataService, "getSomeData").and.returnValue({$promise: getSomeDataDeferred.promise});
...
);
it(...
getSomeDataDeferred.resolve([{obj: "obj1"}, {obj: "obj2"}]);
$scope.$apply();
...
);
With the test described above, the console.log(data) would be testable as the data is accessible from being passed into the .then(). But the data is not immediately available from storeTheData, so storeTheData[0].obj would be undefined. On debug, I can see the data if I go through the promise that was attached to storeTheData via storeTheData.$$state.value
Like I said, I know I could use $httpBackend instead, but is there any way to do this with $q without changing the code under test?
I've not found a way to do this with $q.resolve, but I do have a solution that doesn't involve using the data service or changing the code under test. This is as good, because the main things I wanted to avoid were testing the data service as a side effect and changing the code.
My solution was to create a $resource object via $injector...
$resource = $inject.get("$resource");
...then return that in my basic implementation of the data service. This means I could use $httpBackend to respond to the request to an end point that isn't reliant on the data service's definition staying consistent.
dataService = {
getSomeData: function () {
/* new code starts here */
var resource = $resource(null, null, {
get: {
method: "GET",
isArray: true,
url: "/getSomeData"
}
});
return resource.get();
/* new code ends here */
}
};
...
$httpBackend.when("GET", "/getSomeData").respond(...;

Is it possible to resolve AngularJS promises in a template?

in all documentation and tutorials for HTTP request i found that recomanded usage is something like:
var ax = {
getCaseData: function() {
api.cases.getCase(caseManager.data.id).then(function(res){
// puting my response to $scope.something
$scope.something = res.data;
});
},
}
I really don't feel like senior so please tell me if I am wrong but I thing many times you don't need to store something like this on scope, for example you want to just resolve promise once and display data (you are not doing anything else with it)
So I was thinking if is there an option to make a promise as:
var ax = {
getCaseData: function() {
return api.cases.getCase(caseManager.data.id);
},
}
and after this call tempalte something like:
<li ng-repeat="ax.getCaseData()"></li>
This was handled automatically in old version of AngularJS (<1.2), but was removed since then.
Some posts state that the feature can be re-enabled manually by adding this line in your ".config" function :
$parseProvider.unwrapPromises(true);
But this is not advised as a solution. You are currently doing it the right way.
If you have plenty of cases like this, you can probably create your own "promises wrapper" function, and use it from your template.
See : https://stackoverflow.com/a/19472065/1636977

Set path parameter without affecting payload using $resource

First of all I want to mention that I have been digging around a lot for this. I am unable to find a simple and straight forward answer even in the docs. (Call me dumb if you will, in case it IS mentioned in the docs! I can't seem to find it anyway.)
The thing is, I want to make a PUT request to a URL of the form
app.constant('URL_REL_VENDOR_PRODUCTS', '/api/vendor/:vendorId/products/:productId');
But I do not want to put the vendorId parameter in the request payload. My service layer looks something like this:
services.factory('VendorProductService', function($resource, UserAccountService, URL_BASE, URL_REL_VENDOR_PRODUCTS) {
return $resource(URL_BASE + URL_REL_VENDOR_PRODUCTS, {
vendorId: UserAccountService.getUser().vendorId,
id: '#id'
}, {
update: { method: 'PUT' }
});
});
I know that instead of the vendorId: UserAccountService.getUser().vendorId I could have written something along the lines vendorId: '#vendorId' but then that pollutes my payload doesn't it?
I don't want to keep the mechanism I am already using in the example as the mechanism does not work when you switch accounts i.e.,if the UserAccountService.getUser() is updated. Basically I'm having to reload the entire page to get the service initialized again.
In short, the question is, as the title suggests, how do I set the path parameter vendorId without using a service like the one in the snippet and also without modifying the payload?
Make the parameter value a function:
services.factory('VendorProductService', function($resource, UserAccountService, URL_BASE, URL_REL_VENDOR_PRODUCTS) {
return $resource(URL_BASE + URL_REL_VENDOR_PRODUCTS, {
vendorId: function () {
return UserAccountService.getUser().vendorId
},
id: '#id'
}, {
update: { method: 'PUT' }
});
});
From the Docs:
paramDefaults (optional)
Default values for url parameters. These can be overridden in actions methods. If a parameter value is a function, it will be executed every time when a param value needs to be obtained for a request (unless the param was overridden).
-- AngularJS $resource API Reference

Restangular data, getting into $scope for a list

Been wrestling with API stuff all day, and decided to use Restanglar. Really having issues getting the data out, and into $scope.
I understand that it won't just be the JSON that is returned from the API, and has a bunch of other internal methods etc. But when I get the data out, I can see it buried somewhere in the debugging with console.log, but I can't seem to get it into $scope to use it in my view which was working fine previously.
How can I get that data out into my $scope, and therefore my view?
Model
angular.module('horse', ['restangular'])
.config(function(RestangularProvider) {
RestangularProvider.setBaseUrl('http://url/api');
RestangularProvider.setResponseInterceptor(
function(data, operation, what) {
if (operation == 'getList') {
return data[what];
}
return data;
});
});
Controller
angular
.module('horse')
.controller("IndexController", function ($scope, Restangular) {
$scope.horse = null;
$scope.showSpinner = true;
Restangular.all('horse').getList().then(function(horse) {
$scope.horse = horse;
console.log($scope.horse);
});
});
API response
{"error":false,"horse":[{"id":"1","name":"horse 2"},{"id":"2","name":"horse 2"}]}
Edit 1
Restangular response
[Object, Object, route: "horse", getRestangularUrl: function, getRequestedUrl: function, addRestangularMethod: function, clone: function…]
Edit 2
I have also tried this - https://github.com/mgonto/restangular#using-values-directly-in-templates
$scope.horse = Restangular.all('horse').getList().$object;
Which just results in an empty array being output. I have also tried removing the setResponseInterceptor and modifying the structure of the api to result the data array directly without the meta stuff (error, etc), no joy :(
The data seems to be coming through. I notice you're using Steroids, have you checked the markup and not just the console?
Make sure you set the scope spinner to false, to ensure that the spinner is hidden when the data comes through.
$scope.ShowSpinner = false;
Assuming that what you have shown as "API response" is what's getting outputted from the console.log in your controller, it seems that all you need to do is set your scope model the the property "horse" in the response data like this:
$scope.horse = horse.horse;
Since that reads pretty oddly, you should change the param name of the .then callback to data, which would be a much more agnostic and standard param name. If you make that change you can set your horse data to your scope model from inside your callback like this:
$scope.horse = data.horse;
If I misunderstood your question let me know. Hope this is helpful.

AngularJS Execute function after a Service request ends

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
});

Resources