Undefined $http data when resolved with `.then` method - angularjs

I used the console log which returned the array of data fetched from the server, but when I call the scope on the html I get an undefined error on the console.
app.service('blogpostservice', ['$http', function ($http) {
this.getMoreData = function (pagecount) {
return $http.get('/api/posts/' + pagecount);
}
}]);
app.controller('MainController', ['$scope', 'blogpostservice',
function ($scope, blogpostservice) {
$scope.pagec = 1;
this.getMoreData = function () {
blogpostservice.getMoreData($scope.pagec).then(function (data) {
$scope.posts = data;
console.log($scope.posts);
})
}
this.getMoreData();
}]);
HTML
<h1>{{pagec}}</h1>
<h1>{{posts[1].Title}}</h1>
<div id="posts" class="grid" ng-repeat="post in posts">
<div class=" grid-item">
<div class="blog-post">
<img src="https://placeimg.com/400/400/bbc" alt="">
<h3>{{post.Title}}</h3>
<img ng-src="{{post.Image}}" alt="">
</div>
</div>
</div>

The .then method of an $http promise returns a response object, of which data is one of several properties:
app.service('blogpostservice', ['$http', function ($http) {
this.getMoreData = function (pagecount) {
return $http.get('/api/posts/' + pagecount);
}
}]);
app.controller('MainController', ['$scope', 'blogpostservice',
function ($scope, blogpostservice) {
$scope.pagec = 1;
this.getMoreData = function () {
blogpostservice.getMoreData($scope.pagec)
̶.̶t̶h̶e̶n̶(̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶(̶d̶a̶t̶a̶)̶ ̶{̶
.then(function (response) {
͟v͟a͟r͟ ͟d͟a͟t͟a͟ ͟=͟ ͟r͟e͟s͟p͟o͟n͟s͟e͟.͟d͟a͟t͟a͟;͟
$scope.posts = data;
console.log($scope.posts);
})
.catch(function(response) {
console.log("Error: ", response.status);
throw response;
});
};
this.getMoreData();
}
]);
Also be sure to add a .catch handler to log rejected http requests.
For more information, see AngularJS $http Service API Reference.
UPDATE
i read the doc but connecting to the subject the main problem i made is calling it a data instead of a response on the function right?
The main problem is that the http promise does not resolve data. It resolves a response object. Data is only one of the properties of the response object:
$http(...)
.then(function onSuccess(response) {
// Handle success
var data = response.data;
var status = response.status;
var statusText = response.statusText;
var headers = response.headers;
var config = response.config;
...
})
.catch(function onError(response) {
// Handle error
var data = response.data;
var status = response.status;
var statusText = response.statusText;
var headers = response.headers;
var config = response.config;
...
});
From the Docs:
The response object has these properties:
data – {string|Object} – The response body transformed with the transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
xhrStatus – {string} – Status of the XMLHttpRequest (complete, error, timeout or abort).
A response status code between 200 and 299 is considered a success status and will result in the success callback being called. Any response status code outside of that range is considered an error status and will result in the error callback being called. Also, status codes less than -1 are normalized to zero. -1 usually means the request was aborted, e.g. using a config.timeout. Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning that the outcome (success or error) will be determined by the final response status code.
— AngularJS $http Service API Reference.
Also note that a status of -1 usually is a symptom of a CORS problem. The request being blocked because of a violation of Same Origin Policy.

Rewrite the getMoreData function by using a callback.
See the code sample below
app.service('blogpostservice', ['$http', function ($http) {
this.getMoreData = function (pagecount,callback) {
var result = $http.get('/api/posts/' + pagecount);
if(callback){
callback(result);
}
}
}]);
In essence since you do not know exactly when the getMoreData function will return a value from the http get method you pass the returned data of the http get into a callback function. You then utilize the data from the http get by implementing the callback method in your maincontroller like below:
app.controller('MainController', ['$scope', 'blogpostservice',
function ($scope, blogpostservice) {
$scope.pagec = 1;
this.getMoreData = function () {
blogpostservice.getMoreData($scope.pagec,function(data){ $scope.posts = data; console.log($scope.posts); })
}
this.getMoreData();
}]);
You also need to ensure you that an array is returned from $scope.posts.
Note that in your html you are interpolating {{post}} instead of {{posts}}

Related

Setting timeout in $http Service AngularJS 1.5.5

I have my controller calling the api and by the time the api returns results I have the 500 Internal server in the chrome console popping up. I am using angular 1.5.5, could you please help with some timeout code.
Tried using .timeout(3000,new Error(timeout exceeded)) before .then but it does not compile
angular.module('myApp').factory('submitService',function($http)){
var service={};
service.getJwtToken=function(user)
{
return $http({
method: "POST",
url:"http://localhost:5000/jwtTest",
data: user
}).then(function(resp){
return resp;
});
}
return service;
});
You can try with setInterval
setInterval(function () {
//Call your Service here
}, 5000);
This server error occurs because there may be missing param or something like this
//if 'function2' is dependent on any condition of 'function1' call like this
var f1 = yourService.function1(param1);
f1.then(function (data1) {
if(data1){
var f2 = yourService.function2(param2);
f2.then(function (data2) {
//Do code
});
}
});
//if 'function2' and 'function1' are independent call like this
var f1 = yourService.function1(param1);
f1.then(function (data1) {
//Do code
});
var f2 = yourService.function2(param2);
f2.then(function (data2) {
//Do code
});
To set a timeout of for the $http service, use the timeout property of the config object:
app.factory('submitService', function ($http) {
var service = {};
service.getJwtToken = function (user) {
var config = { timeout: 3000 };
return $http.post("http://localhost:5000/jwtTest", user, config);
};
return service;
});
From the Docs:
config object
Object describing the request to be made and how it should be processed. The object has following properties:
timeout – {number|Promise} – timeout in milliseconds, or promise that should abort the request when resolved.
A numerical timeout or a promise returned from $timeout, will set the xhrStatus in the response to "timeout", and any other resolved promise will set it to "abort", following standard XMLHttpRequest behavior.
For more information, see
AngularJS $http Service API Reference - Arguments

$http.get doesn't work with REST model using then() function

As you guys know, Angular recently deprecated the http.get.success,error functions. So this kind of calls are not recommended in your controller anymore:
$http.get("/myurl").success(function(data){
myctrl.myobj = data;
}));
Rather, this kind of calls are to be used:
$http.get("/myurl").then(
function(data) {
myctrl.myobj = data;
},
function(error) {
...
}
Problem is, simple Spring REST models aren't working with this new code. I recently downloaded a sample code with the above old success function and a REST model like this:
#RequestMapping("/resource")
public Map<String,Object> home() {
Map<String,Object> model = new HashMap<String,Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
}
This should return a map like {id:<someid>, content:"Hello World"} for the $http.get() call, but it receives nothing - the view is blank.
How can I resolve this issue?
The first (of four) argument passed to success() is the data (i.e. body) of the response.
But the first (and unique) argument passed to then() is not the data. It's the full HTTP response, containing the data, the headers, the status, the config.
So what you actually need is
$http.get("/myurl").then(
function(response) {
myctrl.myobj = response.data;
},
function(error) {
...
});
The expectation of the result is different. Its the response and not the data object directly.
documentation says :
// Simple GET request example:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Properties of the response are
data – {string|Object} – The response body transformed with the transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
As the data object is required,
Please convert the code as
$http.get("/resource").then(
function(response) {
myctrl.myobj = response.data;
});
then must be return a new promise so you should handle it with defers.
var myApp = angular.module('myApp', []);
myApp.factory('modelFromFactory', function($q) {
return {
getModel: function(data) {
var deferred = $q.defer();
var items = [];
items.push({"id":"f77e3886-976b-4f38-b84d-ae4d322759d4","content":"Hello World"});
deferred.resolve(items);
return deferred.promise;
}
};
});
function MyCtrl($scope, modelFromFactory) {
modelFromFactory.getModel()
.then(function(data){
$scope.model = data;
})
}
Here is working fiddle -> https://jsfiddle.net/o16kg9p4/7/

Ionic/Angular js Parsing JSON data from server

How to parse JSON object from server using Ionic/Angular js? This is work for me when loading json from /js folder:
var rssApp = angular.module('myApp', []);
rssApp.controller('datas', ['$scope', '$http', function($scope,$http) {
$http.get("js/data.json") // <=== this
.then(function (response)
{
$scope.dat = response;
});
}]);
but when i load json from webserver,i got blank, this is my code :
var rssApp = angular.module('myApp', []);
rssApp.controller('datas', ['$scope', '$http', function($scope,$http) {
$http.get("http://localhost/data.json") //<== this
.then(function (response)
{
$scope.dat = response;
});
}]);
Thanks for your help..
Analyzing what you have done so far, i suppose your data is being returned as an array from the webserver since you are getting blank with your code. So, you should assign the response[0] to $scope.dat.
var rssApp = angular.module('myApp', []);
rssApp.controller('datas', ['$scope', '$http', function($scope,$http) {
$http.get("http://localhost/data.json") //<== this
.then(function (response)
{
$scope.dat = response[0];
});
}]);
First
Angular's $http promise response contains a few properties.
The data returned from the server would be in the data property.
From the Angular docs:
The response object has these properties:
data – {string|Object} – The response body transformed with the
`transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
Also you want to make sure that the endpoint is pointing at the correct resource.
I.e if data.json is inside your application file, then the path would be path/to/data.json. This should be easy to identify, because you'll get a 404 status code with your response object.
Solution
So you might want to do something like:
var endpoint = "path/to/data.json";
$http.get(endpoint).then(function (response) {
$scope.dat = response.data;
});
One more thing
Also, if in doubt user console.debug(response); which will allow you to inspect the response object.

I set the service data,but after $http done, I cannot get the service data

My service define like this:
module.factory('portfolio',function(){
var data;
var selectedPort;
return{
getData: function(){
return data;
},
setData:function(portfolios){
data = portfolios;
},
getSelectedPort:function(){
return selectedPort;
},
setSelectedPort:function(portfolioDetail){
selectedPort = portfolioDetail;
}
}
});
And in my controller the code as follows:
module.controller('portfoliosController', function($scope,$http, alertService,stockService, userDataService, portfolio){
var req = {
method: 'get',
url: 'www.facebook.com',
headers: {
'Authorization': userDataService.getToken()
}
};
$http(req).then(function(reponse){
$scope.portfoliosPriceList = reponse['data'];
portfolio.setData($scope.portfoliosPriceList);
console.log(portfolio.getData())//At here,I can get the portfolio's data
}, function(){
alertService.setMessge("System maintenance , please try again later");
alertService.alert();
});
console.log(portfolio.getData())//At here, I cannot get the portfolio's data
});
the error is
Error: undefined is not an object (evaluating 'message.substr')
Anybody can help me to solve this problem?Actually, I really do not understand, why I cannot get the data outside the $http
The request that you do with the $http service is done asynchronously, so the callback that you pass to the .send is not immediately invoked.
The code that follows (the console.log) is executed just after the $http(req) call is made but before the callback is called when the request is responded.
Maybe you will understand better with an simpler example:
function portfoliosController() {
var data = 'Initial Data. ',
content = document.getElementById('content');
// setTimeout would be your $http.send(req)
// calledLater would be your .then(function() { ... })
setTimeout(function calledLater() {
data = 'Data coming from the server takes some time to arrive...';
content.innerHTML = content.innerHTML + data;
}, 1000);
content.innerHTML = content.innerHTML + data;
}
portfoliosController();
<div id="content">
This is because javascript is asynchronous, so the code:
portfolio.getData()
Is maybe executing before the data is returned from the service.
In this case, you should only use the data of the portfolio just after the request is complete (inside the .then() function of $http) or put a promise.
Here is the documentation for angular promises:
https://docs.angularjs.org/api/ng/service/$q

Angularjs how to cancel resource promise when switching routes

I'm just getting my feet wet with Angularjs. I have an issue which I think has something to do with promises.
Let's say I load route 'A' which makes several ajax requests through it's controller:
allSites = AllSites.query({ id:categoryID });
allSites.$promise.then(function(allSites){
//add stuff to the scope and does other things
//(including making another ajax request)
});
Then I have route 'B' which makes it's own API request through it's controller:
$scope.categories = Category.query();
Here's the factory service currently used by route 'A':
.factory('AllSites',function($resource){
return $resource('api/categorySites/:id');
});
When I first view route 'A' but then switch to 'B' before 'A' is finished loading, route 'B' sits and waits for everything initially requested in 'A' to finish (actually, the query() request is made, but it won't resolve until the one from 'A' does, at that point, the stuff inside .then() continues to happen, even though I don't need it as I'm now on another route.
As you can see in my devtools timeline, the green line indicates when I switched to route 'B'. The request for route 'B' didn't resolve until the two requests above did (a request that is usually very fast). (at which point I'm able to use the view as a user). Then, after that, more promises resolve from route 'A'.
I've searched everywhere for an answer and can only find people that want to "defer" the route loading until promises are resolved. But in my case I almost want the opposite. I want to kill those requests when I switch.
Here's someone else with the same, unanswered question: Reject Angularjs resource promises
Any help is appreciated.
First of all, I decided I needed to use $http since I couldn't find any solution that used $resource, nor could I get it to work on my own.
So here's what my factory turned into, based on #Sid's answer here, using the guide at http://www.bennadel.com/blog/2616-aborting-ajax-requests-using-http-and-angularjs.htm
.factory('AllSites',function($http,$q){
function getSites(categoryID) {
// The timeout property of the http request takes a deferred value
// that will abort the underying AJAX request if / when the deferred
// value is resolved.
var deferredAbort = $q.defer();
// Initiate the AJAX request.
var request = $http({
method: 'get',
url: 'api/categorySites/'+categoryID,
timeout: deferredAbort.promise
});
// Rather than returning the http-promise object, we want to pipe it
// through another promise so that we can "unwrap" the response
// without letting the http-transport mechansim leak out of the
// service layer.
var promise = request.then(
function( response ) {
return( response.data );
},
function() {
return( $q.reject( 'Something went wrong' ) );
}
);
// Now that we have the promise that we're going to return to the
// calling context, let's augment it with the abort method. Since
// the $http service uses a deferred value for the timeout, then
// all we have to do here is resolve the value and AngularJS will
// abort the underlying AJAX request.
promise.abort = function() {
deferredAbort.resolve();
};
// Since we're creating functions and passing them out of scope,
// we're creating object references that may be hard to garbage
// collect. As such, we can perform some clean-up once we know
// that the requests has finished.
promise.finally(
function() {
promise.abort = angular.noop;
deferredAbort = request = promise = null;
}
);
return( promise );
}
// Return the public API.
return({
getSites: getSites
});
});
Then, in my controller (route 'A' from my problem):
var allSitesPromise = AllSites.getSites(categoryID);
$scope.$on('$destroy',function(){
allSitesPromise.abort();
});
allSitesPromise.then(function(allSites){
// do stuff here with the result
}
I wish the factory wasn't so messy, but I'll take what I can get. However, now there's a separate, related issue Here where, though the promise was cancelled, the next actions are still delayed. If you have an answer for that, you can post it there.
There is a similar question with the answer "How to cancel $resource requests".
While it does not address the question exactly it gives all ingredients to cancel resource request when route is switched:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Cancel resource</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-resource.js"></script>
<script>
angular.module("app", ["ngResource"]).
factory(
"services",
["$resource", function($resource)
{
function resolveAction(resolve)
{
if (this.params)
{
this.timeout = this.params.timeout;
this.params.timeout = null;
}
this.then = null;
resolve(this);
}
return $resource(
"http://md5.jsontest.com/",
{},
{
MD5:
{
method: "GET",
params: { text: null },
then: resolveAction
},
});
}]).
controller(
"Test",
["services", "$q", "$timeout", function(services, $q, $timeout)
{
this.value = "Sample text";
this.requestTimeout = 100;
this.call = function()
{
var self = this;
self.result = services.MD5(
{
text: self.value,
timeout: $q(function(resolve)
{
$timeout(resolve, self.requestTimeout);
})
});
}
}]);
</script>
</head>
<body ng-app="app" ng-controller="Test as test">
<label>Text: <input type="text" ng-model="test.value" /></label><br/>
<label>Timeout: <input type="text" ng-model="test.requestTimeout" /></label><br/>
<input type="button" value="call" ng-click="test.call()"/>
<div ng-bind="test.result.md5"></div>
</body>
</html>
How it works
$resource merges action definition, request params and data to build a config parameter for an $http request.
a config parameter passed into an $http request is treated as a promise like object, so it may contain then function to initialize config.
action's then function may pass timeout promise from params into the config.
Please look at "Cancel Angularjs resource request" for details.
Take a look at this post
You could do what he is doing and resolve the promise to abort the request on a route change (or state change if using ui router).
It may not be the easiest thing to make happen but seems like it can work.
I cancel the promise with $q.reject(). I think that this way is more simple:
In SitesServices.js:
;(() => {
app.services('SitesServices', sitesServices)
sitesServices.$inject = ['$http', '$q']
function sitesServices($http, $q) {
var sitesPromise = $q.defer()
this.getSites = () => {
var url = 'api/sites'
sitesPromise.reject()
sitesPromise = $q.defer()
$http.get(url)
.success(sitesPromise.resolve)
.error(sitesPromise.reject)
return sitesPromise.promise
}
}
})()
In SitesController.js:
;(() => {
app.controller('SitesController', sitesControler)
sitesControler.$inject = ['$scope', 'SitesServices']
function sitesControler($scope, SitesServices) {
$scope.sites = []
$scope.getSites = () => {
SitesServices.getSites().then(sites => {
$scope.sites = sites
})
}
}
})()
Checking the docs for $resource I found a link to this little beauty.
https://docs.angularjs.org/api/ng/service/$http#usage
timeout – {number|Promise} – timeout in milliseconds, or promise that
should abort the request when resolved.
I've used it with some success. It go a little something like this.
export default function MyService($q, $http) {
"ngInject";
var service = {
getStuff: getStuff,
};
let _cancelGetStuff = angular.noop;
return service;
function getStuff(args) {
_cancelGetStuff(); // cancel any previous request that might be ongoing.
let canceller = $q( resolve => { _cancelGetStuff = resolve; });
return $http({
method: "GET",
url: <MYURL>
params: args,
timeout: canceller
}).then(successCB, errorCB);
function successCB (response) {
return response.data;
}
function errorCB (error) {
return $q.reject(error.data);
}
}
}
Keep in mind
This assumes you only want the results from the last request
The canceled requests still call successCB but the response is undefined.
It may also call errorCB, the error.status will be -1 just like if the request timed out.

Resources