I have the following code which make seperate requests for jsonp data.
In the code "doRequestA" works fine and returns a result. The issue I have is
I need to catch any errors if they occur. I have tried implementing this in
"doRequestB", but only receive the alert error (I have ommitted the callback from doRequestB).
Here is the fiddle http://jsfiddle.net/a4Rc2/417/
function jsonp_callback(data) {
alert(data.found);
}
function jsonp_example($scope, $http) {
$scope.doRequestA = function () {
var url = "http://public-api.wordpress.com/rest/v1/sites/wtmpeachtest.wordpress.com/posts?callback=jsonp_callback";
$http.jsonp(url);
};
$scope.doRequestB = function () {
var url = "http://public-api.wordpress.com/rest/v1/sites/wtmpeachtest.wordpress.com/posts";
$http.jsonp(url)
.success(function (data) {
alert(data.found);
}).error(function (data, status, headers, config) {
alert('error');
});
};
}
Any advice is greatly appreciated, thanks in advance.
You actually are using $http.jsonp incorrectly in both cases. You just can't see the error in the first case because you are not handling it.
With Angular.js's $http.jsonp method, the callback method is handled automatically. You shouldn't use your own methods in the result string, but rather insert JSON_CALLBACK (exactly as written) into your string. This way, you can handle the response using the promise returned from Angular. If you watch the network activity (say, using Firebug or the developer tools in your browser of choice), you'll see JSON_CALLBACK replaced with something like angular.callbacks._0*.
In the second example, you don't have a callback method defined at all, so the result will always error. There's actually no way for the jsonp result to be handled, since it simply returns the JSON object without a callback method, and the result just is ignored.
Here's a working result: http://jsfiddle.net/tPLaN/1/
The code:
function jsonp_callback(data) {
alert(data.found);
}
function jsonp_example($scope, $http) {
$scope.doRequestA = function() {
var url = "http://public-api.wordpress.com/rest/v1/sites/wtmpeachtest.wordpress.com/posts?callback=JSON_CALLBACK";
$http.jsonp(url).success(function(data) {
jsonp_callback(data);
});
};
$scope.doRequestB = function() {
var url = "http://public-api.wordpress.com/rest/v1/sites/wtmpeachtest.wordpress.com/posts?callback=JSON_CALLBACK";
$http.jsonp(url)
.success(function (data) {
alert(data.found);
}).error(function (data, status, headers, config) {
alert('error');
});
};
}
The only thing I changed was
Correcting the two URLs.
Moving the callback handler on the first method inside the .success() method on the promise.
Believe it or not, the need for JSON_CALLBACK is in the documentation for $http.jsonp, but it's sort of hidden.
* Note, please do not use the replacement for JSON_CALLBACK for anything. It's a private method generated by Angular, I am just showing it to help make more sense of what is happening.
Related
I am consuming this Rest web service from my AngularJS application and I'm using $resource to pass parameters and get my data.
I have a particular service that can take the same parameter multiple times like this in my Postman session:
/myService/Accounts?acctId[]=7029&acctId[]=3324
I've tried to pass a generated string to my $resource query methods placeholder but it ends up escaping all my brackets and not working.
I also tried not using a placeholder and passing the same parameter twice, it doesn't fail but it takes the last value so that does not work either.
$scope.resultsColl = rfpService.searchAccountName.query({
acctName: $scope.accountName,
acctRole: acctRoleParams,
acctRole: acctRoleParams2
});
Hopefully someone has had this same issue? Any ideas?
From the research I've done on other posts it looks like you can't dynamically generate using $resource. My working solution was to switch to $http, this way I could dynamically generate multiple acctRole[] params:
var acctRoleParams = '';
if(corporate === true)
{
acctRoleParams = '&acctRole[]=C';
};
if(smallBiz === true)
{
acctRoleParams += '&acctRole[]=B';
};
$http.get("https://myRestServiceUrl/services/customer/accounts?acct=" + $scope.account + acctRoleParams)
.success(function (data, status, headers, config) {
$scope.gridOptions.data = data.data.detailsByAcct;
})
.error(function (data, status, headers, config) {
showToast("An error occurred during the request");
})
.finally(function () {
$scope.isGridLoading = false;
});
I wanted to post this in case anyone else has been struggling to do the same thing.
I'm calling a method inside WebApi 2 controller everything is working as expected however when I check the developer console I see the following error message:
Error: (intermediate value).error is not a function
Now I've done the research and some people have said: You're missing a closing semi colon within your method, however I've gone through my code and I can't see anywhere that I'm missing this.
My Angular Js login function:
$scope.Login = function () {
var emailAddress = $scope.emailAddress.trim();
var password = $scope.password.trim();
var valid = loginApi(emailAddress, password);
};
Which calls this function:
function loginApi(emailAddress, password) {
var login = { "EmailAddress": emailAddress, "Password": password };
$http.post("/api/Login/", login).success(function (data, status) {
alert('success');
}
.error(function (error, status) {
alert(error);
}));
};
I may be wrong but don't you need a closing parenthesis after your .success function and then remove one at the end of the .error?
$http.post().success(function() {}).error(function() {});
Otherwise it would seem like you're enclosing your error in your success.
The other thing to think about is that in Angular both .success and .error have been deprecated and they suggest using then:
Angular API: $http
The $http legacy promise methods success and error have been deprecated. Use the standard then method instead.
When a user logs into the main page of my site, I typically load quite a bit of data on the home page. much more than when they come to a specific url on the page. When they hit the ome page, that actually fullfills the data requests of much of the data that I grab individually when they hit a specific page.
I like how the $http module works with $cache and I'm wanting to use the knowledge of my home page hit to populate the cache of calls I know the individual page will make.
That is, as an example, my home page calls /rest/clients which returns all clients while individual pages call /rest/client/101. What I want to do is make it so that if /rest/clients is called first, then when /rest/client/101 is called an new fresh xhr call does not have to be made but the data can be gotten from the cache as if /rest/client/101 had already been called.
I've never done a decorator before but I'm thinking maybe a decorator on the $http service? I looked through the $http code and it seemed the cache is stored in closure to the actual http call and not exposed except on the next Get.
Has anyone done this or similar? I could not find it. Any specific pseudo coding suggestions would be very welcome.
In your data service you have 2 methods, getAll and getOne.
In the service define a reference to your getAll results promise.
Then in your getOne service check to see if that promise exists and if it does use it to filter out the one item that you need to satisfy your getOne need.
module.service('dataService', function($http){
var getAllPromise = null;
this.getAll = function(){
if (getAllPromise !== null){
getAllPromise;
}
getAllPromise = $http.get('clients');
return getAllPromise
};
this.getOne = function(id){
if (getAllPromise !== null){
return getAllPromise
.then(function(allData){
//logic here to find the one in the full result set
return theFoundItem;
};
}
return $http.get('clients/' + id);
};
});
I found the solution I asked for but implementing and making it testable is proving to be beyond my skills. I'm going to go with #brocco solution but for the permanent record I'm leaving the actual answer to what I was asking. I'm not marking this as the correct solution because #brocco solution is better for my real problem. So, thank you #brocco for the help.
You can see below what I'm basically doing is to create my own $cache with $cacheFactory. I then use the .put method of the new cache object to prime my cache. Then, subsequent calls to the client/1 url will get the cache'd record without ever having to call cache/1 in real live. The cache is loaded in the for loop from the first big call.
Thanks for everyones input on this.
var myApp = angular.module('myApp', []);
myApp.factory('speakersCache', function($cacheFactory) {
return $cacheFactory('speakersCacheData');
});
myApp.controller('personController', ['$scope','$http','speakersCache', function ($scope,$http,speakersCache) {
$scope.getAllSpeakers = function() {
$http.get('speakers.json',{cache: speakersCache}).
success(function (data, status, headers, config) {
debugger;
var i;
for(i=0;i<data.length;i++) {
var url = 'speaker/' + i;
speakersCache.put(url, data[i]);
}
}).
error(function (data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
};
$scope.getAllSessions = function() {
$http.get('sessions.json',{cache: speakersCache}).
success(function (data, status, headers, config) {
debugger;
}).
error(function (data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
};
$scope.getOneSpeaker = function() {
$http.get('speaker/1',{cache: speakersCache}).
success(function (data, status, headers, config) {
debugger;
}).
error(function (data, status, headers, config) {
debugger;
});
}
$scope.checkit = function() {
var x = speakersCache;
debugger;
};
}]);
If I understand you well, I have done something similar:
I have this code:
.factory('myOwnEntity', ['$filter',
function ($filter) {
var myOwnList = [];
return {
set : function (data) {
myOwnList = data;
},
get : function () {
return myOwnList;
},
find : function (id) {
return $filter('filter')(myOwnList, { itemId : id }).pop();
}
}
}
])
When I make the petition to the Web Service, I store the information like this:
$http.get(url, {
cache : true
})
.success(function (data) {
myOwnEntity.set(data);
defer.resolve(data);
});
return defer.promise;
Now, the next time I need some information, I just query my entity with the find method. Hope this is what you are looking for.
I'm using angular-http-auth to show a login dialog whenever a 401 "unauthorized" response is returned from the server.
Since I'm cool, I also try to deserialize response objects in my services. For example, if a service requests a car and the response is {make: Honda, model: Civic}, I try to deserialize that into a Car object using transformResponse.
For example:
getCar: function() {
return $http.get('/api/car', {
method: 'GET',
transformResponse: function(data, headers) {
var c = angular.fromJson(data);
return new Car(c);
}
});
}
This doesn't work with angular-http-auth. If the response was a 401 Unauthorized, you'll get a javascript error. It's because angular will try to run that transformResponse code even if the response was a 401.
It turns out that $http interceptors (which is what angular-http-auth uses) are run AFTER the transformResponse code. That's a huge problem, because none of that code in transformResponse will work if the server response was a 401 (there wouldn't be any data)
Is this a problem for anyone else? How did you get around it? Am I not to use transformResponse if I use $http interceptors?
Late to the party, I know, but to anyone coming here from Google like I did (I also posted this as a comment on a related issue filed with the Angular repo):
I also found it to be confusing that response interceptors run after the transformResponse method. I added a method to $http.defaults.transformResponse. Here is an example from the documentation on how to do that.
So, if you need to basically have a response interceptor that runs before the transformResponse method, this should do it:
'use strict';
angular.module('app')
.run(function ($http) {
$http.defaults.transformResponse.push(function (data, headers) {
// do stuff here before the response transformation
// Be sure to return `data` so that the next function in the queue can use it.
// Your services won't load otherwise!
return data;
});
});
If your services or http calls don't have their own response transformer, you're good now.
If your services do have their own transformResponse method, they will actually override all default transformers (I found this out after a long read of the documentation), and the above code will not run.
To circumvent this, you can follow this example in the docs.
To get around this problem I don't use transformResponse anymore. I just can't see the point of transformResponse if it runs before $http interceptors.
In order to use angular-http-auth and also deserialize responses in your services, you can write your services so that they execute the HTTP request first and then deserialize the response in a callback function.
As an example, here is how I would have restructured the example in the OP:
Plunker
services.factory('HttpCarService', function($resource, $q) {
var resource = $resource('/api/car');
return {
getCar: function() {
var deferred = $q.defer();
var car = null;
var successCallback = function(data, status, headers, config) {
var c = angular.fromJson(data);
car = new Car(c);
deferred.resolve(car);
};
var errorCallback = function(data, status, headers, config) {
deferred.reject("something wrong");
};
var result = resource.get(successCallback, errorCallback);
return deferred.promise;
}
};
});
This pattern will also work if data is an array.
$http interceptors will run before either of the callback methods are executed. If your $resource needs url params, you can make the getCar() function accept a config object as a parameter, and pass the necessary information on when you make the $resource.get() call.
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.