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);
});
}])
Related
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.
I'm trying to read a properties file in angular, It's neccesary to have this file at the root of my project without any kind of angular stuffs, just a simple file, like java.
This is what I'm doing.
Creating a angular.properties file.
angular.properties
{
"url": "http://localhost"
}
This is my controller where I invoke my service method:
controller
captchaService
.all()
.then(function(data) {
...
}, function (data) {
...
});
This is my service class:
service
'use strict';
angular.module('app.service', [])
.factory('myService',
['$http', '$q',
function ($http, $q) {
var _URL = "";
function getValuesFromProperties() {
$http({
method : 'GET',
url : 'angular.properties'
}).success(function (data, status, headers, config) {
_URL = data.url;
}).error(function (data, status, headers, config) {
...
});
}
function all() {
getValuesFromProperties();
var deferred = $q.defer(),
promise = deferred.promise;
sessionStorage.removeItem('X_CAPTCHA_TOKEN');
$http({
method : 'POST',
url : _URL
}).success(function (data, status, headers, config) {
...
}).error(function (data, status, headers, config) {
...
});
return promise;
}
return {
all : all,
getValuesFromProperties : getValuesFromProperties
};
}]);
But here in my services class I have two $http running on an asynchronous way for that reason I can not get the value of url everything else works perfect. How can resolve this? Maybe there is another way to do this. I don't want to create a constant class because this file it's placed at the root of the project and it will be configure for others, they don't need to enter inside a lot of folders and check the code, search and edit. Any idea?
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
})
};
});
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
}
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; ...