Is it OK to fake a promise on a new angular resource? - angularjs

I'm working with $resource in Angular 1.3.
I have a bunch of controllers with methods that work with the resource objects.
When the state of retrieval matters, these methods use the $promise property of the resource to ensure that they only process the objects after they are retrieved. These all work fine with existing and updated resource objects.
var ProposalResource = $resource(proposalUrl, {id: '#id'}, {'update': {method: 'PUT'}});
The resource objects are obtained by ProposalResource.get({id:....
However, when I create a new resource object in order to make a new object using new ProposalResource(..., the methods fail because the $promise property is undefined.
I've worked around this by setting the $promise property on the new resource object to a resolved promise.
This seems to work OK but it feels like a nasty kludge. The option of explicitly checking for whether or not the $promise property is defined in all the other methods is even less appealing though.
Am I doing the right thing?

I don't know why you need to use ProposalResource, but I usually use the $q provider.
That way you can do a simple function that returns a promise and you can call it from your controller methods.
Example service that uses a promise:
function someServiceMethod(params) {
//do something here, maybe create an object,maybe make a call with $http or something
var obj = createSomeObject(params);
//this resolves the object once the createSomeObject method or function have completed
$q.when(obj);
}
This approach is easier than doing the whole: var deferred = $q.defer(); and return deferred.promise after the deferred.resolves.
If you're using $resource, then I recommend just using $http provider from angular.
Here's $http
Here's $q

As mentioned in the $resource AngularJS Doucmentation:
Class actions return empty instance (with additional properties
below). Instance actions return promise of the action.
The statement above gives you a hint that instance action methods, $get, $save, ... and any other actions that you define in your $resource action definition, will always return a promise.
DEMO
e.g.
var User = $resource('user.json', {}, {
'update': {'method': 'PUT'}
});
var user = new User();
// this sends a GET request to user.json and returns the promise directly
// from the instance action.
user.$get()
.then(function(latestUserData) {
// latestUserData is also an instance of the User resource
return latestUserData.$update({
'name': 'Ryan'
});
})
.then(function(updatedUserData) {
// do whatever you want here
});
As you can see, the instance action $get returns a promise and when that promise is resolved, the .then() callback returns the response data from the server and at the same time it is wrapped/instantiated with the User $resource.

Related

How to get boolean value using $resource of AngularJS and spring rest controller

Trying to send get boolean value from spring restcontroller using $resource service of angular js ,but getting nothing in response of $resource.get.Is there anythingextra i need to add on my client side or server side for getting boolean resource. Below is my code for rest controller and $resource
Rest controller -
#RequestMapping(value="validate/company/{companyName}",method=RequestMethod.GET,produces={"application/json"})
public Boolean validateCouponCode(#PathVariable("companyName") String companyName){
return companyService.exists(companyName, Column.SITE_NAME);
}
$resourse of Angular js -
function validateCompanyFn(companyName)
return $resource("admin/rest/company/validate/company/:companyName")
.get({companyName:companyName}).$promise
}
What i am doing wrong?
please suggest, if there is another way of getting boolean response using $resource, and without using any extra object on server side
$resource does not return booleans
The $resource.get method only returns object references, not booleans.
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data.
Under the hood, the $resource service uses angular.copy to populate the object.
angular.copy
Creates a deep copy of source, which should be an object or an array.
If a destination is provided, all of its elements (for arrays) or properties (for objects) are deleted and then all elements/properties from the source are copied to it.
— AngularJS angular.copy API Reference
Since a boolean has no elements/properties, nothing is copied.
To return a boolean, use the $http service:
//USE $http service
function getSubCategoryIdNameFn(catId){
var url = "admin/rest/subcategory-id-name/" + catId;
return $http.get(url).then(function(response) {
return response.data;
});
};
There is no need to manufacture a promise with $q.defer as $promise already is a promise. Simply return the $promise
//SIMPLIFY
function getSubCategoryIdNameFn(catId){
//var deferred = $q.defer();
//vvvv RETURN the $promise
return $resource("admin/rest/subcategory-id-name/:catId",{catId:"#catId"})
.query({catId:catId})
.$promise
//.then(function(response){
// deferred.resolve(response);
//},function(error){
// deferred.reject(error);
//});
//return deferred.promise;
}

Restangular error interceptor - how to pass a custom object to a controller that does not know about restangular?

I am stuck with the approach I am taking probably due to my lack of knowledge about angular promises VS restangular promises, etc.
I have an AngularJs application with TypeScript (although typescript is mostly irrelevant here and the same applies to any javascript). These are the players:
controller: it gets injected a service, through this service the controller can send a POST to an API
service: it wraps restangular. The idea is that this service does not expose any restangular functionality to the controller. It abstracts the controller from knowing how to save an item. It has a method that accepts an object and returns an angular promise.
export interface IRegistrationService {
addRegistration(model: registration.BusinessRegistration): ng.IPromise<void>;
}
Restangular error interceptor: it handles Http Responses with status 400 coming from an API because they are validation errors and transforms them in a custom object. The idea is that eventually the controller can either succeed saving an item (posting it through the service) or get a validation error (that comes from this interceptor).
This is what I have so far:
The restangular error interceptor
restangularProvider.setErrorInterceptor((response: restangular.IResponse, deferred: ng.IDeferred<any>, responseHandler: any) => {
if (response.status === 400) {
let validationError: myTsd.IApiValidationErrors = getAsValidationError(response.data);
// How to pass this validationError as an errorCallback to the controller?
//deferred.notify(validationError);
//deferred.reject(validationError); //this stops the chain
//return true; // if error not handled. But where to put the validationError?
//return false; // if error handled. But where to put the validationError?
}
});
The service that abstracts the controller from knowing anything about restangular Notice that it should return an angular promise, not a restangular promise.
public addRegistration(model: registration.BusinessRegistration): ng.IPromise<void> {
return this.restangular.all("registration")
.post<registration.BusinessRegistration>(model)
.then(() => {
console.log("Registration posted successfully");
}, (error: any) => {
//if I get the object here, how to make it available in the errorCallback of the controller caller?
}, (notify: any) => {
//if I get the object here, how to make it available in the errorCallback of the controller caller?
});
}
The controller that uses that service but knows nothing about restangular
//public static $inject = ["app.services.RegistrationService"];
//.. controller code
this.registrationService.addRegistration(this.model)
.then(() => {
console.log("model posted successfully in remote API")
}, (error: myTsd.IApiValidationErrors) => {
// if there was any validation error I need the object here
console.log(error);
});
How should I chain everything? My "only" requirements are:
the logic to create that object is in a central place like the setErrorInterceptor, and it should distinguish between http responses 400 or any other. If the response is neither 2xx or 400 it can handle the error or pass it to the service that uses restangular. It doesn't matter
the service that uses restangular must allow the controller to either succeed or have a callbackError with the custom validation error object. It abstracts the controller from everything else.
Thanks a lot!
I don't fully understand the docs here https://github.com/mgonto/restangular#seterrorinterceptor and whether there is something else other than notifying or rejecting that I could do.
Restangular's .setErrorInterceptor() is a rather odd beast, which, as far as I can gather, won't do what you want it to do.
It can be made to sense error code(s) (eg your 400) and do stuff when that condition arises, but has no further ability other than to return false (block) or return anything else (not block).
The non-blocking action allows the promise chain to take its natural, unintercepted course.
The blocking action inhibits both the error path and the success path of the promise chain.
Therefore think of .setErrorInterceptor() as a "selective blocker", not a "filter" or a "catch", and contrast it with promise.catch() behaviour, by which :
an error state can be converted to success by returning some value/object,
the error can be rethrown, or some new error can be thrown, keeping the promise chain on the error path.
The inability of .setErrorInterceptor() to propagate anything other than the original error seems to mitigate against it in favour of a named "catch handler" (eg. getAsValidationError() or a function that wraps getAsValidationError()) that can be included wherever relevant. That should give you the feature you require.
The only problem I can foresee is getting the catch handler to recognise the "400" condition - possibly simple - requires research.
Don't get too hung up on Angular promises versus Restangular. They should inter-operate.

TVRage consume service via AngularJS

i am trying to consume this webservice (http://services.tvrage.com/feeds/show_list.php) from TVRage using Angularjs.
I can 'connect' to the service (using firebug I see GET show_list.php STATUS 200 OK) but when i try to print any data from the response I get none.
This is the code that i use:
var TV_Episodes = angular.module('TV_Episodes', ['ngResource']);
TV_Episodes.controller('GetAllEpisodes', function($scope, $resource) {
var dataService = $resource('http://services.tvrage.com/feeds/show_list.php');
$scope.data = dataService.get();
console.log($scope.data());
});
any ideas on how I can just console.log the the response?
UPDATE 1:
After some more trying i found out that that i get the following error as a response from TVRAGE.
"XMLHttpRequest cannot load http://services.tvrage.com/feeds/show_list.php. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access."
therefor i tweaked my code so
var dataService = $resource('http://services.tvrage.com/feeds/show_list.php?key=xxxx',{},{headers: { 'Access-Control-Allow-Origin': '*' }});
but i still get the same error as before.
$resource.get() returns a promise, which means you are likely printing to the console prior to the data being retrieved. Instead use the appropriate callback function:
$scope.data = dataService.get(function() { console.log($scope.data); });
The get method is asyncronous. When it is called it returns immediately with a reference to an object (or array, if specified - but not a promise as indicated in MWay's answer). Then, later, that same reference is updated with the data that is returned from the server on success. Here's the relevant part from the documentation:
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data. This means that in most cases one never has to write a callback function for the action methods.
As fast as the request might be, it won't resolve until the event loop comes around again. The resource is helpfully designed to free you up from having to worry about writing callbacks. If you need to though, the get method takes callback function parameters that will be invoked when the request resolves and the data is ready.
var TV_Episodes = angular.module('TV_Episodes', ['ngResource']);
TV_Episodes.controller('GetAllEpisodes', function($scope, $resource) {
var dataService = $resource('http://services.tvrage.com/feeds/show_list.php');
$scope.data = dataService.get(function () {
console.log($scope.data());
});
});
Or, you can access the promise used for processing the request by using *$promise", which is a property on empty instance object returned from get.

AngularJS transformResponse

In angularjs resource, I would like to convert my json data into JS objects
//Complex object with inheritance chain
function Car(year, make){
this.make = make;
this.year = year;
}
var carResource = $resource("/api/car/id", {id: '#id'},
{
get: {
method: 'GET',
transformResponse: function(data, headersGetter){
return new Car(data.make, data.year);
}
}
}
)
However this does not seem to be happening
What I am getting back is a $resource object meaning that the properties make and year are set correctly, however the prototype of the returned object points to $resource
Is there a way where I can map my json data directly to my own objects?
Or will I have to write my own 'resource' implementation?
transformResponse is executed on $http level.
When you customise $resource actions with custom config object, that object is actually passed on to the underlying $http service. So if you specify a transformResponse callback, it will be executed on $http level, and the results of your transformation will be passed back on to $resource.
$resource service will instantiate new object from your response data (which is already transformed by the transformResponse callback) and this new object will be an instance of the $resource itself.
So, your car object will be an instance of the Car, but only for a moment, until it's properties are copied into a new $resource object.
Here's a simplistic view of the process:
$resource service initiates request
$http service sends request and receives the response
$http service transforms the response (response is now instance of Car)
$resource service receives the transformed response (from $http)
$resource service makes an instance of itself using transformed response properties (result is now instance of $resource)
Anyway, I don't recommend decorating or extending the $resource service, because it's simpler to write your own implementation using $http service.

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