I have a couple of questions on asynchronous functions in angular. I want my service function to be able to return data I get by using $http and use that data in another function. Let me demonstrate with the current code:
dashboard.servicesModule.service('SubscriptionService', function ($http, $q) {
this.getImportantData= function (param1, param2) {
var url = "my/url/with/parameters?param1=param1¶m2=param2";
$http.get(url).then(function(response){
console.log("response");
console.log(response.data); // < --- this actually shows the data i need!!
return response.data.vsyData; <--- however, this returns undefined
}, function(error){
// some error thingy just in case
});
};
this.getSomeFunctionality = function(param1, param2, param3){
var importantdata= this.getImportantData(param1, param2);
// do some filtering on the data with the purpose of returning only what i need
console.log(importantdata); <--- undefined
};
this.getEvenMoreFunctionality = function(param1, param2, param3){
var importantdata= this.getImportantData(param1, param2);
// reusing getImportantData again!
console.log(importantdata); <--- undefined
};
});
So I have been trying all kinds of things ( like this and this ) and some inventions of my own. But it starts to seem like there is no way of using the data from the $http.get anywhere else then in its own callback. And since i need the endresult in my controller, it seems like there is no other way then perform the $http.get(url).success(...some logic here) in my controller and do the filtering and other manipulation there.
However, I read here and I quote:
Our controller should be almost completely agnostic as to how data is
retrieved and sent, and should only be concerned with the general
action of sending and retrieving data to and from the service.
I interpret this as following: my controller should ask the service for the data I need and the service should make sure that it gives the data in the correct form.
The most important question is: how can I make the above code do what it needs to do or would that be impossible?
And am I supposed to do my business logic in my controller? Because here, in the angular docs I basically read that you should do business logic in your controller, but not filtering...
Its probably obvious, but ... I am an angular newbie :-).
Thanks!
Try this:
this.getImportantData= function (param1, param2) {
var url = "my/url/with/parameters?param1=param1¶m2=param2";
return $http.get(url);
};
this.getSomeFunctionality = function(param1, param2, param3){
this.getImportantData(param1, param2)
.then(function(response){
console.log(response.data.vsyData);
}, function(error){
// some error thingy just in case
});
};
this.getEvenMoreFunctionality = function(param1, param2, param3){
this.getImportantData(param1, param2)
.then(function(response){
console.log(response.data.vsyData);
}, function(error){
// some error thingy just in case
});
};
You can't get data from a method which has an asynchronous call. You can return promise, and then you can get data from promise.
For an assyncronous request you cannot make imediate return statement, tha main javascript thread will not wait for request resolution. I really recomend use $q API, is the angular implementation of Kris Kowal's Q promises/deferred systems.
function getSomething(){
var deferred = $q.defer();
$http.get('foo/bar')
.success(function(data){
$deferred.resolve(data);
});
return deferred.promise();
}
getSomething().then(function(data){
console.log(data);
});
Or you can return directly the $http promise. In case you want to remove the .then() from your controller, you can use $routeProvider with the resolve option that supports $q promises and create routes that self resolve your data, so you just inject them in controller, free of the $q proceedings.
UPDATE 1 - Practical example of resolving dependencies in routes
$routeProvider.when('/items/list', {
templateUrl: '/templates/item/list',
controller: 'ListItemsCtrl',
resolve: {
items: function(ItemsService) {
return ItemsService.getItems(); // getItems returns a promise like 'return $http.get('/items');'
}
}
});
//controller
function ListItemsCtrl($scope, items){
//do something with your items
}
function getSomething(){
var deferred = $q.defer();
$http.get('foo/bar')
.success(function(data){
$deferred.resolve(data);
});
return deferred.promise();
}
getSomething().then(function(data){
console.log(data);
});
Related
I have a separate functions like, submit,update,delete. for all function
$http[method](url)
.then(function(response) {
$scope.successMessage = true;
} , function(response) {
$scope.errorMessageWrong=true;
});
html
<p ng-show="successMessage">Success</p>
<p ng-show="errorMessageWrong"> Something went wrong </p>
For separate functionalities. i need to show the corresponding messages. but i don't want to repeat the code for update, delete and submit and even the same thing for the other pages which do the same operation.
how to create function called errorHandler or something. so that i can reuse it.
can anyone help me
how to create function called errorHandler or something. so that i can reuse it.
Create chainable promises by returning for fulfilled data responses and throwing rejected error responses.
The example function below takes an httpPromise as an argument, puts success or error messages on $scope, and returns a promise suitable for chaining.
function errorHandler(httpPromise) {
var derivedPromise = httpPromise
.then(function onFulfilled(response) {
$scope.successMessage = true;
//return response for chaining
return response;
},
function onRejected(errorResponse) {
$scope.errorMessageWrong = true;
//throw error to chain rejection
throw errorResponse;
});
//return derivedPromise for chaining
return derivedResponse;
};
Then in client code:
var httpPromise = $http[method](url);
errorHandler(httpPromise).then( function (response) {
//use data
});
The client code saves the httpPromise from the $http service call, processes the promise with the errorHandler function, and the uses the derived promise returned by the errorHandler function.
Because calling the then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.1
If you want it global to your app, then you can use an httpInterceptor.
You have to create an interceptor service and then add the interceptor to the $httpProvider in your app.config().
Create your interceptor service:
angular.module('app').factory('myInterceptorService', myInterceptorService);
function myInterceptorService($q){
var errorMessage;
var bShowHideWatchFlag;
return{
requestError: requestError,
responseError: responseError,
showFlag: bShowFlag,
errorMessage: errorMessage
};
function requestError(rejection){
errorMesasge = 'Request error';
bShowHideWatchFlag = true;
$q.reject(rejection);
return;
}
function responseError(rejection){
errorMesasge = 'Response error';
bShowHideWatchFlag = true;
$q.reject(rejection);
return;
}
}
To register with app config, add $httpProvider to app.config
app.config([...,'$httpProvider'...){
$httpProvider.interceptor.push('myInterceptorService');
}
In your controller, you have to bind a watch to the service showFlag:
$scope.$watch( function () { return myInterceptorService.showFlag; },
function (oldval,newval) {
if( oldval!=newval){
$scope.errorMessage = myInterceptorService.errroMessage;
$scope.showMessage = newval;
}
}, true);
You can use service for this and share within various controller. $http is also a service. But if you want to add something more to the data you can create a new service and inject $http to it.
Please forgive me if this is a simply problem for an angular guru, i am fairly new to services.
Below is a snippet of my controller where i have attempted make a service request to call out data from my JSON file "jobs.json".
I am not receiving an data when i load my web page neither i am seeing the JSON file in inspector element.
I assume there's something incorrect in my below code. Does anyone what the issue is?
Click here if you need to play about with the code
"use strict";
var app = angular.module("tickrApp", []);
app.service("tickrService", function ($http, $q){
var deferred = $q.defer();
$http.get('app/data/items.json').then(function (data){
deferred.resolve(data);
});
this.getItems = function () {
return deferred.promise;
}
})
.controller('tickCtrl', function($scope, tickrService) {
var promise = tickrService.getItems();
promise.then(function (data){
$scope.items= getData;
console.log($scope.items);
});
In your Plunkr, you had a few errors, such as the <script> tags around the wrong way (you need to have Angular first, so your code can then use angular.module). You also had the wrong attribute of ng-app-data instead of data-ng-app.
The key problem was with the JS code, the first parameter to the success handler for the $http.get() call is an object with a data property, which is the actual data returned. So you should resolve your promise with that property instead.
Then in the controller, like Michael P. said, getData is undefined, you should use the data parameter passed in.
app.service("tickrService", function($http, $q) {
var deferred = $q.defer();
$http.get('jobs.json').then(function(response) {
deferred.resolve(response.data);
});
this.getjobs = function() {
return deferred.promise;
}
})
.controller('tickCtrl', function($scope, tickrService) {
var promise = tickrService.getjobs();
promise.then(function(data) {
$scope.jobs = data;
console.log($scope.jobs);
});
});
See forked Plunkr.
In the success handler of your getItems function, you are storing getData, which is undefined. You want to store data instead.
Therefore, in the controller, your call to getItems() should be as follows
tickrService.getItems().then(function (data) {
$scope.items = data;
});
Also, you want to make the $http call in getItems. Like that :
this.getItems = function () {
var deferred = $q.defer();
$http.get('app/data/items.json').then(function (data) {
deferred.resolve(data);
});
return deferred.promise;
}
However, you can avoid the above boilerplate code around the promises, because $http.get returns itself a promise. Your service and controller could be much more concise and less polluted by boilerplate code.
The service could be as simple as :
app.service("tickrService", function ($http) {
this.getItems = function () {
return $http.get('app/data/items.json');
}
});
And the controller could be shortened to:
app.controller('tickCtrl', function ($scope, tickrService) {
tickrService.getItems().then(function (response) {
$scope.items = response.data;
})
});
Please note that the response resolved by $http is an object that contains (link to doc) :
data – The response body transformed with the transform functions.
status – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – The configuration object that was used to generate the request.
statusText – HTTP status text of the response.
Therefore in the success handler of getItems we are storing response.data, which is the response body, and not the whole response object.
I'm trying to store some user data in a service that will be accessible and modified across different controllers, and the data will be initially pulled via a $http call. Since the data has to be loaded, I've used promises in my previous code, but this can be quite irritating to write. For instance, even a basic getter function has to be written as the following
UserData.getData().then(function(data) {
//do something with the data
})
where UserData.getData() always returns a promise via deferred.promise (if the data has already been pulled, then resolve immediately). I'm wondering if there is anyway to split a) $http calls, and b) getter and setter methods into two different services so that if I call getter and setter methods from b), I don't need to wrap everything with then?
For instance, I'm trying to make UserFactory in charge of the $http call, and UserData in charge of getters and setters. However, I can't get the code to work since UserData.getData() will return undefined, and wondering if anyone can help? (I don't really want to have to use then everywhere).
angular.module('testApp', [])
//mocks a service that gets data from a server
.factory('UserFactory', function($timeout, $q) {
return {
getData: function() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve({title: 'hello world'});
}, 1000);
return deferred.promise;
}
}
})
.factory('UserData', function(UserFactory) {
var data;
return {
//if already pulled, use existing data
getData: function() {
if (data) return data;
UserFactory.getData().then(function(res) {
data = res;
return data;
})
}
}
})
http://jsfiddle.net/QNLk2/1/
The then method is executed asynchronously so the return method as no effect there. That is why until the data has arrived every call to getData will return undefined.
You can try the following approach, where you can specify a callback if you want to be notified when data is ready, or simply wait for the data to be populated in the return variable.
.factory('UserData', function(UserFactory) {
var data = {};
return {
getData: function(callback) {
UserFactory.getData().then(function(res) {
angular.extend(data, res);
if (callback) {
callback(data);
}
});
return data;
}
}
})
In an $http call, What all things we can pass in the url part, I have a server address, a user name and Password. Can i pass all as a json object?, or we have any other parameter (like url) for this.?
Can someone help me in understanding what is happening on a success call.
Specifically, the code I'm struggling with is:
app.factory('myFactory',function(){
var fact = {};
fact.getData = function(a){
$http({method:'POST',url:'http://100.100.100.100:8080/xx/xx.xx'});
$http.success(function(reply){a(reply)}
);
};
return fact;
});
See the following code, still I am not getting data from server, at the same time no errors too.
xapp.factory('loginData',function($http,$log){
var fact = {};
fact.getData = function(cred,cb){
return
$http({
method:'post',
url:'xxx.xxx.xxx.xxx:xxxx/xxxxxx',
data:cred})
.success(function(data,status,header,config){
cb(data);
})
.error(function(data,status,header,config){
$log.warn(status);
});
};
return fact;
});
xapp.controller('mainController',function($scope,$log,$http,loginData){
$scope.user = {uesr:'user',password:'123'};
loginData.getData($scope.user,function(data){
$scope.reply = data;
});
});
In the console log, I get an 'undefined'. If the http url is correct, do u see any issue?
As far as I understand, a parameter is a callback function that is executed when reply from server is received. This kills the purpose of promises that the $q service provides. Also, $http service itself does not have .success callback. It returns a promise object with .success and .error callbacks. Here's how it should be done:
app.factory('myFactory', function() {
var fact = {};
fact.getData = function() {
return $http.get('your/path/to/resource/');
}
return fact;
})
.controller('myCtrl', function($scope, myFactory) {
myFactory.getData().then(function(response){
//response.data holds your data from server
}, function(){
//this fn gets called when error happens
});
});
Some explanations: myFactory.getData() creates a new http request to the server and returns a promise object which has a method .then(successCallback, errorCallback). You can provide callbacks to the promise to be executed after request is complete.
You might get confused with my mentioned .then(successCallback, errorCallback) and .success(callback) used in your example. A generic promise that $q provides has .then method, but $http service, upon returning a promise, provides shortcuts .success() and .error() but it's the same thing in the end.
This will solve your problem of sending body.
app.factory('myFactory', function($http) {
return{
getData : function(body) {
return $http({
url: 'http://100.100.100.100:8080/xx/xx.xx',
method: 'POST',
data:body
})
}
}
});
app.controller('myCtrl', function($scope, $location, $http, myFactory){
var body ={username:uname, password:pass};
myFactory.getData(body).success(function(data){
$scope.data=data;
});
});
I am trying to build a factory to act as a staging area for my database models, as well as an api to perform basic CRUD calls. I want to be able to access data by storing it in a service or a factory, and keep api methods along with it so I can perform actions like these in the controller.
$scope.folders = Folders.data(); // for a factory
$scope.folders = Folders.data; // for a Service
Folders.create({name: "My Stuff, $oid: { 5fwewe033333 }, user_id: CurrentUser.id"});
Currently I am using the Folder factory like this in the controller.
Folders.foldersData().success( function(data, status) {
$scope.folder = data;
})
.error( function(data,status) {
Flash.warning("There was a problem fetching your data");
});
I know I can just have a promise resolved in the controller, but with the size of the project I'm working on, I like the idea of accessing the Folders model in a service, with out having to make a server call to sync the data every time I make a change.
angular.module('cmsApp')
.factory('Folders', function($http, $q){
var folders = {};
var messageWarn = "Upload Retrival Failed.";
return {
get: function(){
var defered = $q.defer();
$http.get('/folders').success( function ( data, status ) {
defered.resolve(data);
})
.error( function ( data, status ) {
defered.reject();
Flash.warning(message_warn);
});
defered.promise.then( function (promise)
folders = promise;
});
},
data: function (){
return folders;
},
}
});
My problem is that I can't keep the folders object to persist after I call Folders.get(). It always comes back after I call Folders.data() as an empty object.
Is there a way to keep this data stored in the Factory as a up-to-date representation of the Folder model that is not dependent on hitting the server every time?
Running angular 1.2.3, on a Rails 4 API.
You can store the promise in the service as an object on the service. I forked the expanded demo above to demonstrate http://plnkr.co/edit/2HqQAiD33myyfVP4DWg3?p=preview
As with the previous examples, the http call is only made once but this time the promise is added to the folders item on the service object which gets created by the factory.
app.factory('myService', function($http, $q) {
return {
myObject: '',
get: function() {
// Create the deffered object
var deferred = $q.defer();
if(!this.myObject) {
// Request has not been made, so make it
$http.get('my-file.json').then(function(resp) {
console.log('Making the call!');
deferred.resolve(resp.data);
});
// Add the promise to myObject
this.myObject = deferred.promise;
}
// Return the myObject stored on the service
return this.myObject;
}
};
});
In this example, the service essentially IS the data. The first time the service is injected, a promise is created and the call goes out. The service is actually the promise of that data, but when the call comes back, the promise is resolved with the data. So, the second, third, etc time the service is injected, the call isn't made again - the factory has already done its job and returned the service (the promise in this example).
Live demo (click).
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, myService) {
myService.then(function(data) {
$scope.data = data
})
});
app.factory('myService', function($http, $q) {
//this runs the first time the service is injected
//this creates the service
var promise = $http.get('my-file.json').then(function(resp) {
return resp.data;
});
return promise;
});
Here's an expanded demo in which I use the data twice. Note the console log - the call is only ever made once. In this demo, I made the service into an object with a get method that returns the promise. I just wanted to demonstrate another way this technique could be implemented.
app.factory('myService', function($http, $q) {
console.log('Making the call!');
var promise = $http.get('my-file.json').then(function(resp) {
return resp.data;
});
var myService = {
get: function() {
return promise;
}
};
return myService;
});