Problems using $http inside a Service - angularjs

I have a basic data Service which will be used across Controllers. But I'm having an issue grabbing some data that's been added via $http.
Service:
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
$http.get('/json').success(function(resp){
_this.dropdownData.industries = resp.industries;
});
}]);
Controller:
angular.module('core').controller('SignupController', ['$scope', '$http', '$state', 'FormService', function($scope, $http, $state, FormService) {
console.log(FormService.dropdownData); // Shows full object incl industries
console.log(FormService.dropdownData.industries); // empty object {}
}]);
How do I get FormService.dropdownData.industries in my controller?

Create a service like below
appService.factory('Service', function ($http) {
return {
getIndustries: function () {
return $http.get('/json').then(function (response) {
return response.data;
});
}
}
});
Call in controller
appCtrl.controller('personalMsgCtrl', ['$scope', 'Service', function ($scope, Service) {
$scope.Industries = Service.getIndustries();
}]);
Hope this will help

Add a method to your service and use $Http.get inside that like below
_this.getindustries = function (callback) {
return $http.get('/json').success(function(resp){
_this.dropdownData.industries = resp.industries;
callback(_this.dropdownData)
});
};
In your controller need to access it like below.
angular.module('core').controller('myController', ['$scope', 'FormService', function ($scope, FormService) {
FormService.getDropdownData(function (dropdownData) {
console.log(dropdownData); // Shows full object incl industries
console.log(dropdownData.industries); // object {}
});
} ]);

Given that your console log shows the correct object, that shows your service is functioning properly. Only one small mistake you have made here. You need to access the data attributes in your return promise.
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
$http.get('/json').success(function(resp){
//note that this is resp.data.industries, NOT resp.industries
_this.dropdownData.industries = resp.data.industries;
});
}]);

Assuming that you're data is indeed existing and there are no problems with the server, there are quite a few possible solutions
Returning a promise
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
_this.dropdownData.industries = $http.get('/json');
}]);
//Controller
FormService.industries
.then(function(res){
$scope.industries = res.industries
});
Resolving with routeProvider / ui-route
See: $http request before AngularJS app initialises?
You could also write a function to initialize the service when the application starts running. At the end of the day, it is about waiting for the data to be loaded by using a promise. If you never heard about promises before, inform yourself first.

The industries object will be populated at a later point in time when the $http call returns. In the meantime you can still bind to the reference in your view because you've preserved the reference using angular.copy. When the $http call returns, the view will automatically be updated.
It is also a good idea to allow users of your service to handle the event when the $http call returns. You can do this by saving the $promise object as a property of industries:
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
_this.dropdownData.industries.$promise = $http.get('/json').then(function(resp){
// when the ansyc call returns, populate the object,
// but preserve the reference
angular.copy( resp.data.industries, _this.dropdownData.industries);
return _this.dropdownData.industries;
});
}]);
Controller
app.controller('ctrl', function($scope, FormService){
// you can bind this to the view, even though the $http call has not returned yet
// the view will update automatically since the reference was preserved
$scope.dropdownData = FormService.dropdownData;
// alternatively, you can hook into the $http call back through the $promise
FormService.dropdownData.industries.$promise.success(function(industries) {
console.log(industries);
});
});

Related

Resource file is undefined on scope

I have a resource file that sometimes is read, and sometimes is undefined and crashes all the application. I do not know why it only works sometimes and it is causing me trouble to figure a solution out. On my controllers I am passing the service that I created to read the file which is appResourcesService:
app.controller('CommonController', ['$scope', '$location', 'appResourcesService',
function ($scope, $location, appResourcesService)
And the service to read resoruces is the following:
'use strict';
app.factory('appResourcesService', ['$resource', function ($resource) {
var appResourcesServiceFactory = {};
appResourcesServiceFactory.getResources = function ($scope, language, locale) {
var languageFilePath = 'resources/AppResources.json';
$resource(languageFilePath).get().$promise.then(function (data) {
$scope.appResources = data;
}, function (reason) {
var defaultLanguageFilePath = 'resources/AppResources.json';
$resource(defaultLanguageFilePath).get(function (data) {
$scope.appResources = data;
});
});
};
return appResourcesServiceFactory;
}]);
Like I said, sometimes it works without any kind of problem, but sometimes when in the controller I reach the piec eof code $scope.appResources, appResources is undefined. I think it is maybe because it reaches there before the file is read, but I am not being able to find a solution to avoid it.
Your factory should return a promise.
It's the job of your controller to handle data. (I use $http as it's cleaner)
app.factory('appResourcesService', ['$http', function($http) {
var appResourcesServiceFactory = {};
appResourcesServiceFactory.getResources = function () {
var languageFilePath = 'resources/AppResources.json';
return $http.get(languageFilePath).then(function(response) {
return response.data;
});
)};
return appResourcesServiceFactory;
}]);
You resolve the promise inside the controller with .then to get the data and set your scope.
appResourcesServiceFactory.getResources().then(function(data) {
$scope.appResources = data;
})

AngularJS Ctrl As Syntax with Services

I'm migrating my Angular 1.x code to use the newer, more preferred syntax of avoiding using $scope and using the controller as syntax. I'm having an issue with getting data from a service though.
Here's my example:
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', ['$http', '$location', 'userService',
function($http, $location, userService) {
this.name = 'John Doe';
// this.userData = {
// }
this.userData = userService.async().then(function(d) {
return d;
});
console.log(this.userData);
}
]);
myApp.service('userService', function($http) {
var userService = {
async: function() {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('https://jsonplaceholder.typicode.com/users').then(function(response) {
// The then function here is an opportunity to modify the response
// console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
// Return the promise to the controller
return promise;
}
};
return userService;
});
<body ng-app="myApp">
<div id="ctrl-as-exmpl" ng-controller="MainCtrl as mainctrl">
<h1>Phone Numbers for {{mainctrl.name}}</h1>
<button ng-click="">newest</button>
<p>{{mainctrl.userData}}</p>
<ul>
<li ng-repeat="users in mainctrl.userData">{{users.phone}} -- {{users.email}}</li>
</ul>
</div>
</body>
This issue I'm having is that the data returned from the userService.async is an object that I can't seem to drill down thru to get the data from, as the data is a child of $$state, which can't seem to be used in the view.
If this isn't the proper way to use services in the Controller As/$scope-less syntax, what is the proper way?
Live example here: http://plnkr.co/edit/ejHReaAIvVzlSExL5Elw?p=preview
You have a missed an important part of promises - when you return a value from a promise, you return a new promise that will resolve to this value.
The current way to set the data returned from a promise to a local variable in your controller is this:
myApp.controller('MainCtrl', ['$http', '$location', 'userService',
function($http, $location, userService) {
this.name = 'John Doe';
userService.async().then(function(d) {
this.userData = d;
});
}
]);
But now you are in a catch because of the scope of this, so it is common to use a "placeholder" variable for the controller this scope.
myApp.controller('MainCtrl', ['$http', '$location', 'userService',
function($http, $location, userService) {
var $ctrl = this; // Use $ctrl instead of this when you want to address the controller instance
$ctrl.name = 'John Doe';
userService.async().then(function(d) {
$ctrl.userData = d;
});
}
]);
this.userData = userService.async().then(function(d) {
return d;
});
In this bit of code this.userData is actually being defined as a promise. If you want the data that is being returned, you need to use the d parameter from your .then function like so:
userService.async().then(function(d){
this.userData = d;
//a console.log() here would determine if you are getting the data you want.
});
Edit
I just noticed in your Service that you are already using .data on your response object.
Honestly, my best advice to you would be to just return the promise from the http.get() call inside of your service like this:
myApp.service('userService', function($http) {
var userService = {
async: function() {
return $http.get('https://jsonplaceholder.typicode.com/users');
}
};
return userService;
});
And then you could use something like this to capture and utilize the response data:
userService.async().then(function(response){
this.userData = response.data;
);
This may be a bit cleaner of a solution

Angularjs data binding between controller and service

I'm not able to get the data binding between controller and service working.
I have a controller and a factory which makes an HTTP call. I would like to be able to call the factory method from other services and see the controller attributes get updated. I tried different options but none of them seem to be working. Any ideas would be greatly appreciated.
Please see the code here:
http://plnkr.co/edit/d3c16z?p=preview
Here is the javascript code.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.controller('EventDetailCtrl', ['$http', 'EventDetailSvc', '$scope',
function ($http, EventDetailSvc, $scope) {
this.event = EventDetailSvc.event;
EventDetailSvc.getEvent();
console.log(self.event);
$scope.$watch(angular.bind(this, function () {
console.log('under watch');
console.log(this.event);
return this.event;
}), function (newVal, oldVal) {
console.log('under watch2');
console.log(newVal);
this.event = newVal;
});
}])
.factory('EventDetailSvc', ['$http', function ($http) {
var event = {};
var factory = {};
factory.getEvent = function() {
$http.get('http://ip.jsontest.com')
.then(function (response) {
this.event = response.data;
console.log('http successful');
console.log(this.event);
return this.event;
}, function (errResponse) {
console.error("error while retrieving event");
})
};
factory.event = event;
return factory;
}]);
It seems to me that you have nested the event object inside of a factory object. You should be returning event directly instead wrapping it with factory. As it stands now you would need to call EventDetailSvc.factory.event to access your object.

update a service variable within an $http callback

I'm using a service to make user data available to various controllers in my Angular app. I'm stuck trying to figure out how to use the $http service to update a variable local to the service (in my case "this.users"). I've tried with and without promises. The server is responding correctly.
I've read several excellent articles for how to use $http within a service to update the scope of a controller. The best being this one: http://sravi-kiran.blogspot.com/2013/03/MovingAjaxCallsToACustomServiceInAngularJS.html. That does not help me though because it negates the benefits of using a service. Mainly, modifying the scope in one controller does not modify throughout the rest of the app.
Here is what I have thus far.
app.service('UserService', ['$http', function($http) {
this.users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
// this.users is undefined here
console.log(this.users);
}
};
promise.then(function() {
// this.users is undefined here
console.log('this.users');
});
}]);
Any help is greatly appreciated. Thank you.
Try using
var users = [];
rather than
this.users = [];
and see what
console.log(users);
outputs in each of those cases.
Your service is oddly defined, but if you have a return in it you can access it from any controller:
app.service('UserService', ['$http', function($http) {
var users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
// this.users is undefined here
console.log(users);
users = data.data;
}
};
return {
getUsers: function(){
return users;
}
}
}]);
so in your controller, you can use:
var myUsers = UserService.getUsers();
UPDATE to use a service correctly here, your service should return a promise and the promise should be accessed in the controller: Here's an example from another answer I gave
// your service should return a promise
app.service('PickerService', [$http', function($http) {
return {
getFiles: function(){
return $http.get('files.json'); // this returns a promise, the promise is not executed here
}
}
}]);
then in your controller do this:
PickerService.getFiles().then(function(returnValues){ // the promise is executed here as the return values are here
$scope.myDirectiveData = returnValues.data;
});
this does not have scope anymore where you are trying to use it do this instead:
app.service('UserService', [$http', function($http) {
var users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
console.log(users);
}
};
promise.then(function() {
console.log(users);
});
}]);
all local variables to a service should just be vars if you assign them to this as a property than they will be included every time the service is injected into a controller which is bad practice.
I think what your asking for is a solution along the lines of defining your service like this:
angular.module('app')
.service('User', function($http, $q) {
var users = null;
var deferred = $q.defer()
return {
getUsers: function() {
if(users) {
deferred.resolve(users);
} else {
$http.get('users.json');
.success(function(result) {
deferred.resolve(result);
})
.error(function(error) {
deferred.reject(error);
});
}
return deferred.promise;
}
};
});
Then in one Each controller you would have to do this:
angular.module('app')
.controller('ACtrl', function($scope, User) {
User.getUsers().then(function(users) {
// Same object that's in BCtrl
$scope.users = users;
});
});
angular.module('app')
.controller('BCtrl', function($scope, User) {
User.getUsers().then(function(users) {
// Same object that's in ACtrl
$scope.users = users;
});
});
NOTE: Because the deferred.promise the same promise passed to all controllers, executing deferred.resolve(users) in the future will cause all then success callbacks in each of your controllers to be called essentially overwriting the old users list.
All operations on the list will be noticed in all controllers because the users array is a shared object at that point. This will only handle updates to the user list/each individual user on the client side of your application. If you want to persist changes to the server, you're going to have to add other $http methods to your service to handle CRUD operations on a user. This can generally be tricky and I highly advise that you check out ngResource, which takes care of basic RESTful operations

How can I get a service to access server data via an $http call?

I make an $http call inside a service that is supposed to get data from my server. For some reason I can't get my service to work - nothing happens. I know the server code works because if I place the $http call inside a function within my controller, then it gets the server data as expected. Here is the code I have so far:
app.service('myService', function($q,$compile,$http) {
this.getData = function() {
var deferred = $q.defer();
var promise = $http.get('myfile.php').success(function (data) {
var response = $compile(data)($scope);
deferred.resolve(response);
});
return deferred.promise;
};
});
Also, I know the code that uses this service works because if I do something like the following,
app.service('myService', function() {
this.getData = function() {
return 'TEST';
};
});
then I will see the word "TEST" show up in the div that utilizes this service. I feel like I'm very close, but there is something I am missing. Any ideas on what I'm doing wrong?
UPDATE:
controller: function($scope, $http, $rootScope, myService){
var scope = $rootScope;
scope.viewMyData = function() {
var element = myService.getData();
$('#myDiv').html(element);
}
}
HTML:
<div ng-click="viewMyData()">Click Here</div>
<div id="myDiv"></div>
If I strip the code in myService and simply return TEST (as above), then I will see "TEST" show up in id="myDiv". But for some reason the $http call isn't being triggered.
#tymeJV is right, but here's my attempt to spell out the example better. $http returns a promise interface that allows you to chain callbacks to be executed when the $http response returns. So, in this case, calling myService.getData() can't return the result immediately (it's off getting the data from the server), so you need to give it a function to execute when the server finally responds. So, with promises, you simply attach your callback using the thePromise.then(myCallbackFunc).
Service
app.service('myService', function($q,$compile,$http) {
this.getData = function() {
var promise = $http.get('myfile.php');
promise = promise.then(function (response) {
return response.data;
});
return promise;
};
});
Controller
controller: function($scope, $rootScope, myService){
var scope = $rootScope;
scope.viewMyData = function() {
myService.getData().then(function(data) {
$('#myDiv').html(element);
});
}
}
Use .then in the controller to continue the promise pattern:
myService.getData().then(function(data) {
$('#myDiv').html(data);
});

Resources