I am quite new to Angular and I'm not sure how to accomplish what seems to be a simple task. I want to retrieve a JSON file using $http.get, and then use items from that array to create a URL to retrieve more JSON files to display. I know that my code is incorrect, but I think it helps to show what I am trying to accomplish.
app.factory('posts', ['$http', function($http) {
var topStor = $http.get('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty');
var stor = $http.get('https://hacker-news.firebaseio.com/v0/item/'
+ topStor[0] + '.json?print=pretty');
return stor
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}]);
Try this:
app.factory('posts', ['$http', function($http) {
var topStor = $http.get('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty');
return topStor
.success(function(){
var stor = $http.get('https://hacker-news.firebaseio.com/v0/item/'
+ topStor[0] + '.json?print=pretty');
return stor
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
})
.error(function(){
});
}]);
HTTP requests are async by default. Angular uses a promise pattern to handle the asynchronous response from $http.
var topStor = $http.get(...); // topStor is now the promise and not the value
The success method will be called on the returned promise when the request is successful. So to get the array from the original request:
$http.get(URL).success(function(data, status) {
// data is the array from the response
});
A nested request can then be made from the data received from the original request.
$http.get(URL).success(function(data, status) {
$http.get(UTL + data[0]).success(function(innerData, innerStatus) {
// celebrate with innerData
}).error(function(errData, errStatus) {});
}).error(function(errData, errStatus) {});
Note: success and error are special methods added to the $q service that Angular uses.
Since the returned value of calling the $http function is a promise, you can also use the then method to register callbacks, and these callbacks will receive a single argument – an object representing the response. See the API signature and type info below for more details.
A response status code between 200 and 299 is considered a success status and will result in the success callback being called. Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning that the error callback will not be called for such responses.
You can use $q to your advantage here. Return a promise that resolves with the data you're after:
app.factory('posts', ['$http', '$q', function($http, $q) {
function getStory() {
var deferred = $q.defer();
$http.get('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty')
.success(function(data, status, headers, config) {
$http.get('https://hacker-news.firebaseio.com/v0/item/'
+ data[0] + '.json?print=pretty')
.success(function (data, status, headers, config) {
deferred.resolve(data);
})
.error(function(data, status, headers, config) {
deferred.reject('There was a problem getting the story');
});
})
.error(function(data, status, headers, config) {
deferred.reject('There was a problem getting the top stories');
});
return deferred.promise;
}
return {
getStory: getStory
}
}]);
Related
I have made two factory and calling first from second one, consuming second one factory in controller but instead of getting data as JSON, I'm getting data as $$State.
I'm new to angularJS tried many ways but not able to solve, please help.
app.factory('userDetails', ['APIService', '$q', function (APIService, $q) {
getDetails: function () {
var deferred = $q.defer();
var getFunc = function () {
APIService.doGetCall('Masters/employee/username/tarun')
.then(
function (data)
{
deferred.resolve(data);
},
function (data, status, headers, config)
{
deferred.reject('There was an error creating object'); })
return deferred.promise;
}
var a = getFunc();
return a;
}
}
vm.user = userDetails.getDetails();
console.log("user",vm.user);
response is as per below snippet
Response
You should be using .then function to get response there. Rather I'd say you don't need to create extra promise, its an anti-pattern. Where you could utilize the promise return by doGetCall method directly.
app.factory('userDetails', ['APIService', '$q', function(APIService, $q) {
getDetails: function() {
var getFunc = function() {
return APIService.doGetCall('Masters/employee/username/tarun');
}
var a = getFunc();
return a;
}
}]);
vm.user = userDetails.getDetails().then(function(response){
console.log(response.data);
vm.user = response.data;
console.log("user", vm.user);
}, function(error){
console.log(error)
}).catch(exceptionHandler);
userDetails.getDetails() returns a promise, Hence you need to use .then(onsuccess, onRejected)
userDetails.getDetails().then(function(data){
vm.user = data;
console.log("user",vm.user);
});
You've got the
return deferred.promise;
line inside the APIService.doGetCall function. It should be outside of it.
I have created the following angular service for the purpose of making the API calls.
(function (module) {
module.service('APIService',function ($http, $q) {
console.log("Reached APIService")
var deferred = $q.defer();
this.getData = function (requestURL) {
console.log("Reached APIService #GET", requestURL);
$http.get(requestURL).success(
function (data, status) {
deferred.resolve(data);
console.log("In service",status,data);
}).error(
function (data, status) {
deferred.reject(data);
console.log(status);
});
return deferred.promise;
};
this.postData = function (requestURL, requestObj) {
console.log("Reached APIService #POST", requestURL, requestObj);
$http.post(requestURL, requestObj).success(
function (data, status) {
deferred.resolve(data);
console.log(status);
}).error(
function (data, status) {
deferred.reject(data);
console.log(status);
});
return deferred.promise;
};
});
}(angular.module("MainApp")));
I have injected it in my two controllers. However, I am facing following issues:
When I call it first time in first controller, it works fine and returns the desired result. However, when I call it in second controller as follows:
APIService.getData(Config + headerURL).then(function (response) {
console.log(Config + headerURL);
console.log("header response from API", response);
},function(error) {
console.log("API call for header data failed");
});
Since my service returns a promise object, I don't expect the code inside .then() to work before the service data arrives.
However, it runs before service data arrives (no clue how). The most strange thing is that the response that I get inside .then() is not actually the response from this URL (Config+headerURL), but it is the response that was obtained from a different URL previously called in first Controller using the same APIService service.
Just to inform: the actual response from current URL do arrive at a later stage.
I am aware of asynchronous callings and I think it has something to do in this case, but I guess .then() handles it in Angular. So I am confused what is the issue here. Can anyone shed some light please?
Since the service is a singleton, you only have a single instance of the deferred object.
Once resolved, it will keep being resolved, so the next time you call, getData, it will return immediately.
You could move:
var deferred = $q.defer();
inside both your getData and postData function.
Or you could just return the promise that $http creates.
Just try this, You need to return to the http result.
this.getData = function (requestURL) {
console.log("Reached APIService #GET", requestURL);
return $http.get(requestURL) };
Looks like you have to disable cache explicitly within $http call.
$http.get({
url: requestURL,
cache: false
})
You are using an anti-pattern by creating your own promises when $http already returns a promise.
Get rid of $q altogether in this service and simply return $http
this.getData = function (requestURL) {
console.log("Reached APIService #GET", requestURL);
return $http.get(requestURL).then(function (response) {
return response.data;
console.log("In service", status, data);
}, function () {
// errror handling here
});
};
I need to get localeData value print out side of below function.
I know how to print in side of function like what i did here.
I have tried this but didn't work.
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
console.log(localeData);
}).error(function(err) {
alert('warning', err.message);
});
//need to get that value here.
console.log(localeData);
EDIT
Actually i need to do is this
app.factory('customLoader', function($http, $q, SERVER_URL) {
return function(options) {
var deferred = $q.defer();
// do something with $http, $q and key to load localization files
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
//do something here to localData
}).error(function(err) {
alert('warning', err.message);
});
deferred.resolve(localeData);
return deferred.promise;
};
});
This is what i need. finally i need to send localeData.
You don't need to create & resolve a defer yourself. There is an easier way. (Also in the example you give, you resolve promise before the underlying ajax completed)
Angular.js $http service follows $q interface too! So you can write:
app.factory('customLoader', function($http, $q, SERVER_URL) {
return function(options) {
var promise = $http.post(SERVER_URL + 'getLocaleData')
.then(function(localeDataBag) {
var localeData = localeDataBag.data; // .data of 'then' equals to what .success returns.
modifiedLocaleData = localeData++; // do something here to localData
return modifiedLocaleData; // This is the new result
})
.catch(function(err) {
console.log(err);
return $q.reject(err); // This is the new result if something failed. Since we are chaining promises, we should keep failure state.
});
return promise;
};
});
You don't need $q to accomplish your objective:
app.factory('customLoader', ["$http", "SERVER_URL", function($http, SERVER_URL) {
return function(options) {
return $http.post(SERVER_URL + 'getLocaleData');
};
}]);
Because $http and all it's convenience methods actually return a promise. In this case, you can use this service as follows:
customLoader(options).success(function(localeData){
//do something here to localData
}).error(function(err){
alert('warning', err.message);
});
If you really must use $q, then this should do:
app.factory('customLoader', ["$http", "$q", "SERVER_URL", function($http, $q, SERVER_URL) {
return function(options) {
return $q(function(resolve, reject){
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
//do something here to localData
resolve(localeData); // ultimately you ought to resolve inside this function
}).error(function(err) {
alert('warning', err.message);
reject(localeData); // ultimately you ought to reject inside this function
});
});
};
}]);
JS is asynchronous, which basically means that your last console.log will be executed before your server returned any data. So you have to do it inside both your promises:
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
console.log(localeData);
}).error(function(err) {
console.log(err);
alert('warning', err.message); //Just in case you didn't know, this line supposes that your server returns a JSON object with a "message"-property
});
That way, your server-response will be console.log'ed both on success and failure.
Reference
Edit: If you really wanna do it the other way:
var response; //First, declare a variable in the parent scope.
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
response = localeData; //Assign the response data to the variable.
console.log(localeData);
}).error(function(err) {
alert('warning', err.message); //Just in case you didn't know, this line supposes that your server returns a JSON object with a "message"-property
});
console.log(response);
The example won't evaluate as you expect, though. The last console.log will just log undefined.
By following way you can access your server response outside factory in the controller:
app.factory('customLoader', function ($http, $q) {
return {
response: {},
getLocalData: function (param) {
var deferred = $q.defer();
$http.post(SERVER_URL + 'getLocaleData', param).
success(function (data, status, headers, config) {
deferred.resolve(data);
}).
error(function (data, status, headers, config) {
});
return deferred.promise;
}
}
});
app.controller('yourCtrl', function($scope,customLoader) {
$scope.GetLocaleData= function() {
customLoader.getLocalData({
Role: role,
Email_Id: $scope.Email,
Pwd: $scope.Password
}).then(function(localeData) {
$scope.Data = localeData;
});
}
});
localeData is limited to the scope of .success function. You cant get it outside unless you assign it some other variable.
var data = ""
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
data = localeData;
}).error(function(err) {
alert('warning', err.message);
});
//need to get that value here.
console.log(data);
But dont expect to get the desired value in data until you get the response back. $http.post is async
Take a look at promises to overcome such issues, because they are used all over the place, in angular world.
UPDATE:
As you have done in the edit, you may either use $q service like:
app.factory('customLoader', function($http, $q, SERVER_URL) {
return function(options) {
var deferred = $q.defer();
// do something with $http, $q and key to load localization files
$http.post(SERVER_URL + 'getLocaleData').success(function(localeData) {
//do something here to localData
}).error(function(err) {
alert('warning', err.message);
});
deferred.resolve(localeData);
return deferred.promise;
};
});
OR, you may simply return $http.post which itself returns a promise
app.factory('customLoader', function($http, $q, SERVER_URL) {
return function(options) {
return $http.post(SERVER_URL + 'getLocaleData');
};
});
In either way, you are returning a promise object and not just the actual response you get from server. The localeData can be accessed as follows:
customLoader().success(function(res){
console.log(res);
}).error(function(){
//show some error message
})
I wrote a angular js service which returns the response. But when I tried to call the service from the controller I am not able to get the exact response.
app.service('testservice', function($http){
var details= {};
details.getResponse = function() {
return $http({
url: 'send/getdata',
method: 'GET'
}).success(function(data, status, headers, config) {
this.getdata = data;
return getdata;
});
};
return details;
});
I am using the below controller to call the method.
app.controller('testcontroller',[ '$scope', '$http','testService',function($scope, $http,testService) {
var targetresponse = testService.getResponse();
alert(JSON.stringify(targetresponse))
})]);
I am getting the below response,
{"$$state":{"status":0,"pending":[[{"promise":{"$$state":{"status":0}}},null,null,null]]}}
Kindly, let me know the error here.
Your testservice service getResponse method should return a promise, and you can continue that promise chain inside your controller.
Service
details.getResponse = function() {
return $http({
url: 'send/getdata',
method: 'GET'
}).then(function(res) {
this.getdata = res.data;
return res.data;
});
};
You can use resolve that promise and get access of data inside .then function
var targetresponse = testService.getResponse();
targetresponse.then(function(data){
alert(JSON.stringify(data))
})
Srv.getData().then(function (success) {
var data = success.data;
$scope.myData = data;
}, function (error) {
});
Best way to to this.
Hello I'm new to AngularJs and trying to get json Data from my server. When the service gets executed, I see a clear response in Fiddler. But the success or the error function does not get executed, and so the service returns an empty array.
var app = angular.module('imageApp', []);
app.service('imageService', function ($http) {
var imageModels = [];
this.Images = function (url) {
$http({ method: 'GET', url: url }).success(function (data) {
imageModels = data;
}).error(function (data) {
imageModels = data || "Request failed";
});
return imageModels;
};
};
});
app.controller('ImageController', function (imageService, $scope) {
$scope.fetch = function (url) {
$scope.url = url;
$scope.ImageModels = imageService.Images($scope.url);
};
});
$http returns results asynchronously. So, return imageModels will execute before the success() or error() callbacks have had a chance to run. You need to wait for the promise that $http creates to resolve -- use then() in your controller.
Rather than repeat it here, see the solution from #Pete BD: https://stackoverflow.com/a/12513509/215945
Update your service like this.
app.service('imageService', function ($http, $q) {
this.Images = function (url) {
return $http({ method: 'GET', url: url }).success(function (data) {
$q.resolve(data);
}).error(function (data) {
$q.reject(data || "Request failed");
});
};
});
Then your controller, you can now wait for a promise to be returned from the service.
imageService.Images($scope.url)
.then(function(response){
// success
}).catch(function(error){
// error
})
So what happened? On your service, notice that we added $q to create a promise so we can reslove and reject the response from $http and make it "thenable" on the controller.
Writing a service to handle $http request is always a hassle for me, so I created this angular module to handle things for me. If you want you can check it out.