Can a call to $http be made more concise? - angularjs

I find myself frequently using this construct in my controllers:
$http({method: 'GET', url: '../ajax/mycallback', cache: false}).
success(function(data) {
$scope.data = data;
});
Is there any way to make this more concise? In particular, is there a kind of "default success method" that just stores the result on the scope? Also, is there a way to globally set cache to false, so I can use $http.get() ?

Option 1: you only need to pass the value to $scope
$http returns a promise, so you can share the returning result directly with the scope:
$scope.data = $http({method: 'GET', url: '../ajax/mycallback', cache: false})
Angular has automatic promise fulfillment support in templates. So if you assign the data to templates, it will automatically trigger the digest cycle upon fulfillment (read: it will trigger the update when the $http will succeed).
But in this case I would suggest you to use $resource instead of $http and incapsulate the $resource creation as a service.
You also can use $resource the same way, as it has natural automatic promise fulfillment. Natural states that promises will be replaced with actual values (this logic includes arrays support!):
$scope.data = MyResourceService(...)
Option 2: you have a more complex callback
You can simply create a factory function to create a closure within a specific scope:
function cb(scope, scopeObjName) {
return function(data){
scope["scopeObjName"] = data;
// common logic goes here
}
}
and use it like this:
$http({method: 'GET', url: '../ajax/mycallback', cache: false})
.success(cb($scope, "data"))
As you can see, the answer depends on the actual usecase and can be improved further.

You could do the following:
myApp.config(function($httpProvider){
$httpProvider.defaults.cache = false;
});
as for your callback, If that is all you're doing, the anonymous function is the best option. Otherwise you'd have to bind a generic function every http invocation and pass it a context..like this:
$http.get(...).success(standardSuccess.bind({$scope:$scope,p:'data'}));
function standardSuccess(data){
this.$scope[this.p] = data;
}
Then you would just keep that in an injectable somewhere. But as you can see is more verbose and less readable. And slower.
Another option is to pass the scope into the httpConfig itself. and use an interceptor to set your property. (as the config is received in an interceptor. Roughly like:
myApp.config(function($httpProvider){
$httpProvider.defaults.cache = false;
$httpProvider.interceptors.push('defaultHttpSetter');
});
myApp.factory('defaultHttpSetter',function($q){
return {
'response': function(response){
response.config.scope[response.config.prop] = response.data;
return $q.when(response);
}
};
});
Which would be used like..
$http.get('/myurl',{scope:$scope,prop:'data'});

I don't have issue with the other answers, but just in case they are over-thinking it. You can do this:
var genericSuccessFunction = function(data) {
$scope.data = data;
}
You can create your result function as part of the controller and save it as a controller local variable.
Then you just need to pass the result function into the success:
$http({method: 'GET', url: '../ajax/mycallback', cache: false}).success(genericSuccessFunction);
In my experience, I often find myself using this approach for an error function, but I don't often find myself able to use generic success functions.

Related

Ensuring all Api's has been loaded in a page in AngularJS

I have 4 controllers in a page in AngularJS. Each controller calls Api's via http request(scope $http). I want to ensure that all the Api's has been called and loaded till then I can show the loading gif image. How to check all the Api's has been loaded in the page in AngulaJS.
I am not sharing the exact code some variable and name I have modified.
myApp.controller('testController',function ($scope, $http, $q, $filter) {
var _promises = {};
_promises['abc'] =
$http({
url: API_URL+'abc-type/',
method: 'GET',
params: {'test1': 'test2'}
});
_promises['abc1'] =
$http({
url: API_URL+'abc-type2/',
method: 'GET'
});
}
$q.all(_promises).then(function (res) {
alert("All promises executed.");
});
});
$http uses promises ($q) for it's API. You can use the $q.all method to run a callback when an array of $http requests are resolved (you will need to make sure all service requests return promises to avoid undefined behavior).
Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
It would look something like this
$scope.showLoadingGif = true;
$q.all([MyService.makeGet(), MyService.makeAnotherGet(), ...]).then(function(responses) {
// all the calls have returned a response by this point
$scope.showLoadingGif = false;
})

Angular JS 1.X Access Resolved Promise Objects values

I have read a lot of posts on promises,resolving promises, and accessing the data however I cannot seem to. Following some posts on Stack Overflow has just caused errors, so I am not sure what exactly I am doing wrong.
I have a function like so:
function getJsonl() {
var deferred = $q.defer();
$http({
url: 'urlNotShownForSecurity',
dataType:"json",
method: 'GET',
data:{"requestId":"123"},
headers:{"Content-Type":"application/json","requestId":"123"},
}).success(function(data) {
deferred.resolve(data);
console.log(data)
}).error(function(error,status,headers,config) {
deferred.reject(error);
});
return Promise.resolve(deferred.promise);
}
Here I return a json promise that has been resolved resulting in a json object I believe.
Printing to console I get the following:
Inside data is the information I need, it looks like this:
data:Array[8]
0:Object
description:"My description paragraph"
I have tried things with the returned object in my controller like:
vm.result = data.data[0].description;
vm.result = data[0].description
I have tried many different approaches in the view as well to access but I get 2 blank li tags and that is it.
I would like to be able to access the data so I populate a table. So if I can use it with ng repeat that would be great, as well as being able to access without because some data is used in more than just the table.
Update
#DanKing, following your implementation I get the following output in console:
Now I am back with a promise object.
It looks to me as though you're fundamentally misunderstanding the nature of promises.
$http() is an asynchronous function - that means it doesn't complete straight away, which is why it returns a promise.
It looks to me as though you're trying to call $http() and then get the result back and return it from your getJson1() method, before $http() has finished executing.
You can't do that. Your getJson1() method should just return the promise, so your calling method can chain onto it - like this:
getJson1().then(function(data) {
// do some other stuff with the data
});
The whole point of promise chains is that they don't execute straightaway - instead you provide callback functions that will be executed at some indeterminate point in the future, when the previous operation completes.
Your getJson1() function just needs to do this:
return $http({
url: 'urlNotShownForSecurity',
dataType:"json",
method: 'GET',
data:{"requestId":"123"},
headers:{"Content-Type":"application/json","requestId":"123"},
});
getJsonl().then(function(data){
console.log(data);
},function(err){
console.log(err);
})
should work. Where is your $http request and where is your call to getJsonl() will also make a difference. So choose that carefully when implementation. If you are using this in a service then you will have to return the function result say
this.somefunction = function (){
return getJonl();
}
and in your controller inject the service and do the following
service.somefunction().then(function(data){
console.log(data);
},function(err){
console.log(err);
})
Ok, rewrote the answer as a complete component to show the moving parts.
$http returns a promise so your original getJsonl call can be simplified. Using your original $http parameters this should dump your API response to the screen if you use the <json-thing> tag:
angular.module('yourModuleName')
.component('jsonThing', {
template: '<pre>{{$ctrl.thing|json}}</pre>',
controller: function ($http) {
var $ctrl = this;
getJsonl()
.then(function (response) {
console.log(response); // we have the $http result here
$ctrl.thing = response.data; // allow the template access
}, function (error) {
console.log(error); // api call failed
});
// call backend server and return a promise of incoming data
function getJsonl() {
return $http({
url: 'urlNotShownForSecurity',
dataType: 'json',
method: 'GET',
data: { requestId: '123'},
headers: { 'Content-Type': 'application/json', requestId: '123'}
});
}
}
});

Passing a path with '/' to $resource or $http factory

Is there a way to pass a parameter containing / to Factory? Want to accomplish something like
.factory('MyData', ['$resource', function ($resource) {
return $resource('http://1.2.3.4/:urlFragment', {
urlFragment : '' // default empty
}, {
getData : {
method : 'GET'
},
And calling it
$scope.scopeVar = MyData.getData({urlFragment : '/some/path/to/data'});
Looking at the network console, I see that / are replaced with %2.
Can I encode the passed parameter inside Factory? (Using $http or $resource).
Or in general, how can I execute any functions on parameters inside factory?
No, you can't really get access to the url inside of your factory because $resource automatically handles it. But thankfully Angular gives you a way to get access to the url before it is called by using the $resource directly. Looking at the docs here, one of the actions you can supply in your $resource declaration is a transformRequest property.
return $resource('http://1.2.3.4/:urlFragment', {urlFragment: ''}, {
getData: {method: 'GET', transformRequest: function(data, headers){
// make your modifications here to either data or headers
}}
});
Although I haven't actually run this code, I believe that should allow you to do what you want. Let me know if it doesn't.

How to refresh / invalidate $resource cache in AngularJS

I have a simple User $resource that uses the default $http cache implementation like so:
factory('User', function($resource){
return $resource(endpoint + '/user/current/:projectId', {},
{get:
{
cache: true,
method: 'GET'
}
}
);
})
This works very well, i.e. my server is only called once in my application, then the value is fetched from cache.
But I need to refresh the value from the server after a certain operation. Is there an easy way to do that?
Thanks.
Keep the boolean and get the $http cache:
var $httpDefaultCache = $cacheFactory.get('$http');
Then you can control it like any another cache made with $cacheFactory, a usage instance provided below:
$httpDefaultCache.remove(key);
// Where key is the relative URL of your resource (eg: /api/user/current/51a9020d91799f1e9b8db12f)
Instead of using a boolean argument in the cache property of each action you can pass on a cache instance created with $cacheFactory which you can have more control over (i.e. clear the cache).
Example usage:
app.factory('Todos', function($resource, $cacheFactory) {
var cache = $cacheFactory('todo');
return $resource(apiBaseUrl + '/todos/:id', { id: '#id' }, {
'get': { method: 'GET', cache: cache },
'query': { method: 'GET', cache: cache, isArray: true }
});
});
I came across this thread looking for something similar, but found that $resource will manage the cache for you automatically, so there's no need to force the cache to be cleared.
The idea is that if you have a resource that you can query, that query response will be cached, but if you save something for that same resource, the previously cached data must be invalid, so it is cleared for you. It makes sense that it would work this way.
Here's some code I use to do this (you can ignore the possibly odd-looking factory creation part and pay attention to the "class" body).
'use strict';
sampleApp.players.$ng.factory('sampleApp.players.PlayerService', [
'$log',
'$resource',
sampleApp.players.PlayerService = function ($log, $resource) {
var service = {};
$log.info('Creating player resource.');
var Player = $resource('/api/players', {}, {query: {
isArray: true,
cache: true,
method: 'GET'
}});
service.addPlayer = function(playerName) {
$log.info('Saving a new player.');
return new Player({name: playerName}).$save();
};
service.listPlayers = function () {
$log.info('Fetching players.');
return Player.query();
};
return service;
}]);
If you call the listPlayers function several times, the first call makes a http get request and all subsequent calls are cached. If you call addPlayer though, a http post is performed as expected, and then the next call to listPlayers will perform a http get (not cached).
This keeps you out of the business of managing someone else's ($http) cache and trying to keep up with which url's are being used for requests and which are clearing caches at the right times.
I suppose the moral of the story here is to work with the library and all will be well... except for any bugs or incomplete features, but Angular doesn't have any of those ;)
p.s. This is all running on AngularJS 1.2.0.

use promise to get only portion of ajax response

The tutorial and docs show this very convenient syntax for AJAX calls:
$scope.myvar = $resource(/*blah blah*/);
Is there a way to use this syntax (instead of having to create a new successfn every time) if my API doesn't return myvar but something like {"myvar": myvar}?
UPDATE: Ah, now I think I got what you're after: You want to have the empty reference (placeholder) for a field of the returned object, in a call like this:
var User = $resource('/user/:userId', {userId:'#id'});
Am I right?
If so - I think the easiest way is simply to bind to the object's field (aka {{user.myvar}}).
Well, the success function is called only once (and if) the data has been received successfully. The function is called with the fetched data given to it as an argument and you can access it no matter its structure. You can reuse a function instead of defining one inline, of course.
So instead of this (example from Angular docs):
$http({method: 'GET', url: '/someUrl'}).
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
You can use:
function myHandler(data) {
// Do something with data.myvar ...
}
$http({method: 'GET', url: '/someUrl'}).success(myHandler);
Hopefully this helps, but very possibly I did not get what exactly you're after :) Let me know.
Relevant documentation.
You need to call get() or query() on your resource to make an actual AJAX request. If you don't want to assign the full result of the server API directly to some ($scope) variable, then you have to write the success function.
var SomeResource = $resource('/blah/blah');
var theResource = SomeResource.get({optional params here}, function() {
$scope.myVar = theResource.myVar;
}
or
var SomeResource = $resource('/blah/blah');
SomeResource.get({optional params here}, function(theResource) {
$scope.myVar = theResource.myVar;
}
Or do what #Chasseur recommends -- assign the full result then bind to the appropriate field in your view:
var SomeResource = $resource('/blah/blah');
$scope.theResource = SomeResource.get({optional params here});
Then in HTML use {{theResource.myVar}}.
Also, $resource, unlike $http, does not return a promise. Update: in newer versions of Angular, $resource now exposes a promise.

Resources