How to handle Angular $http result in a Factory - javascript

I am using a Angular factory, service and a controller. I use the service to $http get data from a url, send it to the factory who then handles the response data and sends a object to the controller:
Service:
module.exports = function($http, $q){
return {
getOptions: function () {
var deferred = $q.defer();
$http({ method: "GET", url: AppAPI.url + 'acf/v2/options/' })
.success(function (data, status, headers, config) {
deferred.resolve(data);
}).error(function (data, status, headers, config) {
deferred.reject(status);
});
return deferred.promise;
}
}
};
Factory:
module.exports = function(optionsService){
var options = {};
var getOptions = optionsService.getOptions();
getOptions.then(function(items){
options = items;
});
return options;
};
Controller:
module.exports = function($scope, $http, optionsFactory){
console.log(optionsFactory);
};
But the Factory returns an empty options to the controller. How would you assign the items from the promise to the options object which get's returned to the controller?
UPDATE
Here is a fiddle
Would appreciate the help :)

Could you try changing
var getOptions = optionsService.getOptions();
// to
var getOptions = optionsService.getOptions;
If you created a plunkr/fiddle, that would help a bit.

Related

AngularJS: Get $http response from function in module

how do i get the response from $http in from a function in a module?
Angular module:
// module customServices
var customServices = angular.module("customServices", []);
// object for response
httpResponse = {content:null};
// function sendRequest
function sendRequest(param)
{
// inject $http
var initInjector = angular.injector(['ng']);
var $http = initInjector.get('$http');
// set header
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
$http({
// config
method: 'POST',
url: 'response.php',
data: $.param(param),
// success
}).success(function (response, status, headers, config) {
httpResponse.content = response;
// error
}).error(function (response, status, headers, config) {
console.warn("error");
});
}
// factory - moduleService
customServices.factory("moduleService", function () {
return {
// function - members
members: function(param)
{
switch(param.fc)
{
// getAll
case 'getAll':
sendRequest({
service :'members',
fc : param.fc,
id : param.id
});
return httpResponse;
}
},
};
});
Controller:
myApp.controller('main', ['$scope', '$http', 'moduleService', function($scope, $http, moduleService){
$scope.handleClick = function () {
var ReturnValue = moduleService.members({
fc:'getAll',
id:'123',
});
console.log(ReturnValue);
};
}]);
the object is on the first click empty and on the second click its content is the $http response.
but i want that the controller knows when the $http response is available.
i tried to use $broadcast and $on, but it seems to be impossible to use $rootScope in my function "sendRequest".
A couple of things:
Why are you defining the httpResponse instead of just returning something from the sendRequest function?
Why are you defining a function outside angular instead of putting it as a service or factory?
You should create a service with the sendRequest method inside like this:
customServices.factory("yourNameHere", function($http, $q) {
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
return {
sendRequest: function sendRequest(param) {
return $http({
method: 'POST',
url: 'response.php',
data: $.param(param),
})
.then(function(response) {
return response;
})
.catch(function(response) {
console.warn("error");
return $q.reject(response);
});
}
};
});
Then in the other service:
customServices.factory("moduleService", function (yourNameHere) {
return {
// function - members
members: function(param)
{
switch(param.fc)
{
// getAll
case 'getAll':
return yourNameHere.sendRequest({
service :'members',
fc : param.fc,
id : param.id
});
}
},
};
});
Now the result will be a promise, so you can consume the data like this:
moduleService.members({
fc:'getAll',
id:'123',
})
.then(function(result) {
console.log(result);
});

not getting data from $http from service to scope

we are trying to get data from service agrService with $http its working but when i reccive data to controller i am not able to access it outside that function
$scope.resource return data inside function but not outside please help.
var app = angular.module('app', ['ui.router','ngTasty']);
app.config(['$urlRouterProvider', '$stateProvider',function($urlRouterProvider, $stateProvider, $routeProvider, $locationProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
templateUrl: 'templates/home.html',
controller: function($scope, $http, $location, agrService) {
agrService.bannerSlides().then(function(data) {
//its working here
$scope.resource = data;
}, function(error) {
// do something else
});
I NEED TO ACCCESS DATA HERE CAN ANY BODY HELP
console.log($scope.resource);
}
});
}]);
app.service('agrService', function($q, $http) {this.bannerSlides = function() {
var dataUrl = 'http://WWW.EXP.COM/codeIgniter_ver/main/home';
var ret = $q.defer();
$http({
method: 'GET',
dataType: "json",
url: dataUrl
})
.success(function(data, status, headers, config) {
ret.resolve(data);
}).error(function(data, status, headers, config) {
ret.reject("Niente, Nada, Caput");
});
return ret.promise;
};
});
My suggestion would be to rethink the logic a bit. You want to do something with the data after you receive it, so why not make a function that you call once the data is received?
You'll never be able to access the resource data in that console log, simply because $http is an async call, and no matter if you return a promise or not, it's simply not ready at that point.
However, if you use it in a template or elsewhere that uses angular's double binding, it will work just fine.
To fix your issue, you can define a function with what happens after that service call and simply call it from the success callback:
agrService.bannerSlides().then(function(data) {
//its working here
$scope.resource = data;
myAfterFunction(); // <--- here
}, function(error) {
// do something else
});
and the function can be:
function myAfterFunction() {
console.log($scope.resource);
}
And btw. your service is an example of deferred antipattern, you can simply do this:
app.service('agrService', function($q, $http) {this.bannerSlides = function() {
var dataUrl = 'http://WWW.EXP.COM/codeIgniter_ver/main/home';
return $http({
method: 'GET',
dataType: "json",
url: dataUrl
})
};
});

ngResource query returns strange objects

I am new to AngularJS and wanted to try out the $resource functionality.
I have this code:
.factory('GetTasksService', function($resource, BASE_URL) {
return $resource(BASE_URL + 'api/tasks');
})
.controller('TasksCtrl', function ($scope, $http, BASE_URL, GetTasksService) {
$scope.tasks = GetTasksService.query();
$scope.getTasks = function () {
$http({ url: BASE_URL + 'api/tasks', method: 'GET' })
.success(function (data, status, headers, config) {
$scope.tasks = data;
})
.error(function (data, status, headers, config) {
alert("call did not work");
});
}
});
The getTasks function works as expected - returning an array of:["taska", "taskb"] as it should.
The GetTasksService.query() however returns an array of [{"0":"t","1":"a","2":"s","3":"k","4":"a"}, {"0":"t","1":"a","2":"s","3":"k","4":"b"}]
Can anyone tell me what I am doing wrong?
There is an option when specifying an action for an ngResource called isArray;
The default action query sets this as true by default, so you need to set set the action like:
query : {
method : 'GET',
isArray : false
}

AngularJs $scope doesn't update after a GET request on a factory

I have been trying AngularJS for a experimental project and I came along with this problem.
In my html I want to display a list of items
Index.html
<h1>Some list</h1>
<div ng-controller="datlist">
<div ng-repeat="item in items">
<div>Item description: {{item.description}}</div>
<div>Item name: {{item.name}}</div>
</div>
</div>
At first I was using a simple controller to get the information and update the view just using this:
controllers.js (original)
function datlist($scope,$http){
$http({method: 'GET', url: 'http://localhost:61686/getdatlist?format=json', headers: {'Access-Control-Allow-Origin': 'localhost:*'}}).
success(function(data, status, headers, config) {
$scope.items=data.itemsToReturn;
console.log(data);
}).
error(function(data, status, headers, config) {
console.log("fail");
});
}
This was working pretty well and I could get the list of items. Whilst, by changing my structure to use a factory to make the same request and bind it to $scope.items it doesn't work. I tried a lot of variations of $watch but I couldn't get it to update $scope.items. I found something about $apply but I really can't understand how to use it.
controllers.js (new one)
var datModule = angular.module('datModule',[]);
datModule.controller('datlist', function ($scope, datfactory){
$scope.items = datfactory.getlist();
$scope.$watch($scope.items, $scope.items = datfactory.getlist());
});
datModule.factory('datfactory', function ($http){
var factory = {};
factory.getlist = function(){
$http({method: 'GET', url: 'http://localhost:61686/getdatlist?format=json', headers: {'Access-Control-Allow-Origin': 'localhost:*'}}).
success(function(data, status, headers, config) {
console.log(data.itemsToReturn); //I get the correct items, all seems ok here
return data.itemsToReturn;
}).
error(function(data, status, headers, config) {
console.log("fail");
});
}
return factory;
});
Any ideas about this will be great.
PS: I found a lot of posts talking about this issue but none of them helped me to get a full solution.
Thanks
Using a watch for that is kinda ugly.
try this:
datModule.factory('datfactory', function ($http, $q){
this.getlist = function(){
return $http.get('http://localhost:61686/getdatlist?format=json',{'Access-Control-Allow-Origin': 'localhost:*'})
.then(function(response) {
console.log(response); //I get the correct items, all seems ok here
return response.data.itemsToReturn;
});
}
return this;
});
datModule.controller('datlist', function ($scope, datfactory){
datfactory.getlist()
.then(function(arrItems){
$scope.items = arrItems;
});
});
This is how you use promises for async matter.
UPDATE (15.01.2015): Now even sleeker!
The issue is nothing to do with the scope digest cycle. You are trying to return from inside a callback directly, which is not asynchronously possible.
I recommend you either use a promise, or return the http promise directly.
var factory = {};
factory.getlist = function(){
return $http({method: 'GET', url: 'http://localhost:61686/getdatlist?format=json', headers: {'Access-Control-Allow-Origin': 'localhost:*'}});
}
return factory;
To return the promise directly, and handle the success/fail at factory.getlist().success()
Alternatively, use your own promise if you want to wrap additional logic around the request.
var datModule = angular.module('datModule',[]);
datModule.controller('datlist', function ($scope, datfactory){
$scope.items = [];
datfactory.getlist().then(function(data) { $scope.items = data });
});
datModule.factory('datfactory', function ($http, $q){
var factory = {};
factory.getlist = function(){
var defer = $q.defer();
$http({method: 'GET', url: 'http://localhost:61686/getdatlist?format=json', headers: {'Access-Control-Allow-Origin': 'localhost:*'}}).
success(function(data) {
// alter data if needed
defer.resolve(data.itemsToReturn);
}).
error(function(data, status, headers, config) {
defer.reject();
});
return defer.promise;
}
return factory;
});
try to initialize $scope.items = []; at controller, before call $http
I hope it helps you.
Well it looks perfect but you can use $apply like this.
datModule.controller('datlist', function ($scope, datfactory){
$scope.$apply(function() {
$scope.items = datfactory.getlist();
});
});
I think another elegant solution to this problem could be - if you are using one of the routing libraries, in my case it is the UI-Router, but could be also ngRoute, is making your controller dependent on the response of the promise, eg. adding a resolve property to the adequate state/route which doesn't let the controller load until the promise is solved and the data is ready, so in your config:
.state('datpage', {
url: '/datpage',
controller: 'DatpageController',
resolve:{
datData: function (datfactory) {
return datDataService.getData("datDataParam");
}]
},
templateUrl: 'views/datpage.html'
})
And inject the datData dependency in your controller, where you can apply it directly to the $scope:
.controller('DatpageController', function ($scope,datData) {
$scope.datPageData = datData; ...

AngularJS returning data from service to view

I'm having difficulty returning data from my service to my view in AngularJS. Here is what I have at the moment. I also tried something like this, but had the same result. AngularJS Load Data from Service
Controller
.controller('ClassCtrl', ['$scope', '$stateParams', 'ClassService', function($scope, $stateParams, ClassService) {
$scope.data = ClassService.getClass($stateParams.siteID, $stateParams.classDate, $stateParams.classID);
$scope.$apply(); // I thought this my do it?
}])
Service
.factory('ClassService', ['$http', function ($http) {
return {
getClass: function(parm1, parm2, parm3) {
var classData = {};
var url = 'http://somesoapservice.com';
var sr = '<?xml version="1.0" encoding="UTF-8"?>' +
'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
'moresoapstuff' +
'</soap:Body>' +
'</soap:Envelope>';
$http({
method: 'POST',
url: url,
data: sr,
headers: {
'Content-type': 'text/xml',
'SOAPAction': 'http://somesoapservice/soapaction'
}
}).
success(function (data, status, headers, config) {
var classDetail = $(data).find('Class');
var staffFirst = classDetail.find('Staff').find('FirstName').text();
//alert(staffFirst); // This displays the first name. Great!
classData = {
staffFirst: staffFirst
};
return classData;
}).
error(function(data, status, headers, config) {
alert('Error!');
});
return classData;
}
}
}])
View
<view title="navTitle" right-buttons="rightButtons">
<content has-header="true" has-footer="true">
First Name: {{data.staffFirst}}
</content>
</view>
I can see staffFirst in an alert when put inside the service, but cannot get it to appear in my view.
I've just started learning AngularJS and this was one of those gotcha's that I missed coming from a desktop synchronous-based development background.
The issue here is that your $Http call within your service is firing off asynchronously. Thus your code line return classData; at the end of your getClass is going to return before the $Http call. Therefore only the empty initialised value ({}) of your object will be returned and that's all your view will have to access.
What you need, is to hook into the promise capability of the Angular framework. A promise essentially allows you to place a method callback on itself so that when an asynchronous operation is complete, your code logic is assured to fire in a synchronous manner.
Your code would need to change as follows:
We first need to inject the promise library into your factory
.factory('ClassService', ['$http','$q' function ($http, $q) {
Your factory return will be:
getClass: function(parm1, parm2, parm3) {
var classData = {};
var url = 'http://somesoapservice.com';
var sr = '<?xml version="1.0" encoding="UTF-8"?>' +
'<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap
/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
'moresoapstuff' +
'</soap:Body>' +
'</soap:Envelope>';
var deferred = $q.defer(); // our promise object
$http({
method: 'POST',
url: url,
data: sr,
headers: {
'Content-type': 'text/xml',
'SOAPAction': 'http://somesoapservice/soapaction'
}
}).
success(function (data, status, headers, config) {
var classDetail = $(data).find('Class');
var staffFirst = classDetail.find('Staff')
.find('FirstName').text();
//alert(staffFirst); // This displays the first name. Great!
classData = {
staffFirst: staffFirst
};
deferred.resolve(classData); //At this point our promise is "resolved"
return deferred.promise;
}).
error(function(data, status, headers, config) {
deferred.reject("Error!");
return deferred.promise;
});
}
Your controller
.controller('ClassCtrl', ['$scope', '$stateParams', 'ClassService', function($scope, $stateParams, ClassService) {
ClassService.getClass($stateParams.siteID, $stateParams.classDate, $stateParams.classID)
.then(function(data){
$scope.data = data; //This callback will only execute once the $http success has completed.
})
.fail(function(error){
alert(error);
});
}])

Categories