In angularJS, I have written a directive to display data. This data is fetched by a http service from remote database.
If I apply below timeout service in code, then result is displayed on the html page as http service returns in one second.
$timeout(function () {
$scope.treeRoot = $scope.$eval('serviceResult') || {};
}, 1000);
If i do not use timeout service, then page does not show any data because $scope.treeRoot is empty e.g.
$scope.treeRoot = $scope.$eval('serviceResult') || {};
In production, the http service can take time more than one second to return, is there any generic solution.
You should use Angular's promises or $http service which allows to return a promise.
After the promise, e.g. $http request, is resolved, you can populate $scope.treeRoot with the results.
Let me know if that helps.
AS suggested in one answer you have to use the $http promises and capture the result in response.
Here is an example with some code.
Service:
app.factory('myService', ['$http',
function($http){
var obj = {};
obj.get = function (q) {
return $http.get('/your_get_url')
};
obj.post = function (q, object) {
return $http.post('/your_post_url', object)
};
return obj;
}
]);
In your controller,
allControllers.controller('changePasswordController', ['$scope','myService',
function($scope,myService) {
myService.get().then(function (result) {
$scope.treeRoot = 'use your result here'
},function error(result)
{
console.log(result)
})
}
])
This replaces your timeout issue.
Related
In the documentation (version 1.1) of AngularJS about interceptors, the interceptor functions all return something like this
return response || $q.when(response);
However, in my app, 'response' is always defined, so $q.when(response) is never executed.
So the question is in what situation will the 'response' be undefined and what will
$q.when(response) // == $q.when(null)
do! because response is undefined/null ?
$q.when(promise) → promise
$q.when(nonPromise) → a new promise, that will asynchronously resolve to the given value nonPromise.
Lets see what is $q.when:
$q.when = function (foreignPromise) {
var deferred = $q.defer();
foreignPromise.then(function (data) {
deferred.resolve(data);
$rootScope.$digest();
}, function (reason) {
deferred.reject(reason);
$rootScope.$digest();
});
return deferred.promise;
}
Factory return $q.when(data)
As we can see $q.when receives promise or nonPromise and wrap it with.
Factory example:
fessmodule.factory('Data', ['$resource','$q', function($resource, $q) {
var data = [
{
"PreAlertInventory": "5.000000",
"SharesInInventory": "3.000000",
"TotalSharesSold": "2.000000",
"TotalMoneySharesSold": "18.000000",
"TotalSharesBought": "0.000000",
"TotalShareCost": "0.000000",
"EstimatedLosses": "0.000000"
}
];
var factory = {
query: function (selectedSubject) {
return $q.when(data);
}
}
return factory;
}]);
Now we can call it from controller:
Data.query()
.then(function (result) {
$scope.data = result;
}, function (result) {
alert("Error: No data returned");
});
Demo Fiddle
Factory returns $q.when(data) || data
From this example we return promise. So lets change it a bit:
Instead return $q.when(data); we will write:
return $q.when(data) || data;
It will work as well. But not vice versa.
As I understand Angular knows that controller waits from Data service the promise and above mentioned statement will use 1st off $q.when(data).
Demo 2 Fiddle
Factory returns data || $q.when(data)
Now lets call our Data service by this way:
$scope.data = Data.query();
No promises, the call is sync.
Out factory seems like:
fessmodule.factory('Data', ['$resource','$q', function($resource, $q) {
var data = [
{
"PreAlertInventory": "5.000000",
"SharesInInventory": "3.000000",
"TotalSharesSold": "2.000000",
"TotalMoneySharesSold": "18.000000",
"TotalSharesBought": "0.000000",
"TotalShareCost": "0.000000",
"EstimatedLosses": "0.000000"
}
];
var factory = {
query: function (selectedSubject) {
return data || $q.when(data);
}
}
return factory;
}]);
Demo 3 Fiddle
My Conclusion
The return data || $q.when(data) means that our service can return single value or promise. But since we know what type of data our service returns , there is no sense in this statement. Or data or promise.
The statements:
return config || $q.when(config);
//AND
return response || $q.when(response);
Are shown in V1.1 of the Documentation of interceptors. It was removed from later versions of the documentation. I think it was a misguided attempt to document that interceptor functions can return either a value or a promise.
The statements otherwise make no sense as config and response always have a reference.
I am using 2 service in controller
First Service is to get AjaxResponse where logic to fetching the response is mentioned
The second Service calls the first service to make Http request and get result and then, in turn, return it to the controller
Ctrl Dependency injected firstService,secondService
this.getService = secondService.getData(param);
First Service--> firstService
this.httpResponse(param){
var re = $http.get(param);
return re.then(success,fail);
}
function success(data){
return data;
}
function fail(data){
console.log(data);
}
Second Service (Dependency injection of First Service)
function secondService(firstService){
this.getData = function(param){
return firstService.httpResponse(param);
};
}
this.getService is coming as undefined, all the call are going properly.
Even tried the following code:
secondService.getData(param).then(function(data){console.log(data);});
That doesn't help either.
You should chain the promises in these kinds of situations.
First, you define your service. It should contain two distinct functions. As an example, I did a GET and a POST.
angular.module("myApp",[]).factory("SharedServices", function($http) {
return {
getItem: function() {
return $http.get('path/to/api');
},
postItem: function(payload) {
return $http.post('path/to/api', payload);
}
};
});
Then, reference the service in your controller. getItem() will return a promise, where you can use it using .then to call your second service in the success callback.
angular.module("myApp",[]).controller("MainCtrl", function($scope, SharedServices) {
SharedServices.getItem().then(function(response) {
//success, got something
//call the second part
var payload = { myPayload: response.data.someItem };
SharedServices.postItem(payload).then(function(response) {
//success
}, function(response) {
//an error has occurred to the second call--POST
});
}, function(response) {
//an error occurred to the first call--GET
});
});
Used Callback to get the result.It is similar to deferred(promise)
I seem to be having an issue sending json from my factory out to controllers
Here is my factory
.factory("UserService", function($http) {
var LevelsHere;
$http.get("/assets/images/generated.json").success(function(data){
LevelsHere = data;
return LevelsHere;
});
return {
all: function() {
return LevelsHere;
},
first: function() {
return LevelsHere[0];
}
};
})
I am simply trying to send the json object out (or bits of it) with this factory. I can console.log inside the http get and it seems to be grabbing the json just fine. I seem to have hit a wall, any help would be much appreciated. I would just like the all ad first functions to be working. thanks!
I first had success by hard coding the levelsHere above it with the json string like var levelsHere = [{"stuff in here"}], but when i moved it over to an $http it doesn't work.
Since you don't have any $watch to look over the value returned from asynchronous $http.get request, the updated value is not available to the consumer. As $http.get request returns a promise, you can leverage the promise and update the value on success of the promise in then() as below:
var app = angular.module('app', [])
.factory("UserService", function($http) {
var LevelsHere = $http.get("https://api.github.com/users/mralexgray/repos")
.success(function(data){
return data;
});
return {
all: function() {
return LevelsHere;
}
};
})
.controller('controller', function(UserService, $scope){
UserService.all().then(function(data){
$scope.value = data;
});
})
DEMO
What is not working exactly? My guess is that you got undefined immediately after this factory method, since $http uses deferred object
You are returning LevelsHere before the async call is finished. The order of your operation goes:
call http.get
return all and first which return LevelsHere (even though the http request has not finished)
http get returns json
success call back fires returning LevelsHere to nobody.
A better way is to just return the promise:
return $http.get("/assets/images/generated.json")
then in your controller you can get the value from the promise by calling the success function. If you try to resolve the promise in the factory and return the value, your controller will try to use the value before it's returned from the server.
var promise = UserService()
promise.success(function(data){
// do something with data
}
I have a wcf service method that gets some data and I call it using Microsoft Ajax library.
To share this data I create a dataService, and many controllers use this service.
I want every controller to get the same data after first call of getData is done, unless somebody need to refresh data and set forceRefresh to true.
My code fails because with the initialize of application 3 controler call dataService.getData and for all there start a new request. How can I make wait dataService.getData calls until the first one is finished and get same result for other subsequent ones..
angular.module('app', []).factory('dataService', function ($q) {
var data= [];
var getData= function (forceRefresh) {
console.log('getFolders called: ', reports.length);
var deferred = $q.defer();
if (forceRefresh || data.length < 1) {
WcfService.GetData(function(result) {
data= result;
deferred.resolve(data);
}, function(ex) { console.log(ex); });
} else {
deferred.resolve(reports);
}
return deferred.promise;
};
return {
getData: getData
};
});
One way would be to cache the promise, rather than the data, so it gets cached when it is created and not when the data arrives. After all, this sounds like your use case.
angular.module('app', []).factory('dataService', function ($q) {
var deferred = null;
var getData= function (forceRefresh) {
console.log('getFolders called: ', reports.length);
if(!forceRefresh && deferred) return deferred.promise;
deferred = $q.defer();
WcfService.GetData(
function(result) { deferred.resolve(data); }, // I'd promisify at a
function(ex){ deferred.reject(ex); } // higher level probably
);
return deferred.promise;
};
return {
getData: getData
};
});
how about setting a global flag in the $rootscope when a controller is querying for data, which can be checked before fetching the data by all the controllers, hence, avoiding redundant calls. The same flag can be put down when any of the controller has the promise fulfilled and data has been fetched, which can then be shared amongst all the controllers.
I found exactly what I search for
Promise queue for AngularJS services
http://inspectorit.com/tips-tricks/promise-queue-for-angularjs-services/
I am using some data which is from a RESTful service in multiple pages.
So I am using angular factories for that. So, I required to get the data once from the server, and everytime I am getting the data with that defined service. Just like a global variables. Here is the sample:
var myApp = angular.module('myservices', []);
myApp.factory('myService', function($http) {
$http({method:"GET", url:"/my/url"}).success(function(result){
return result;
});
});
In my controller I am using this service as:
function myFunction($scope, myService) {
$scope.data = myService;
console.log("data.name"+$scope.data.name);
}
Its working fine for me as per my requirements.
But the problem here is, when I reloaded in my webpage the service will gets called again and requests for server. If in between some other function executes which is dependent on the "defined service", It's giving the error like "something" is undefined. So I want to wait in my script till the service is loaded. How can I do that? Is there anyway do that in angularjs?
You should use promises for async operations where you don't know when it will be completed. A promise "represents an operation that hasn't completed yet, but is expected in the future." (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise)
An example implementation would be like:
myApp.factory('myService', function($http) {
var getData = function() {
// Angular $http() and then() both return promises themselves
return $http({method:"GET", url:"/my/url"}).then(function(result){
// What we return here is the data that will be accessible
// to us after the promise resolves
return result.data;
});
};
return { getData: getData };
});
function myFunction($scope, myService) {
var myDataPromise = myService.getData();
myDataPromise.then(function(result) {
// this is only run after getData() resolves
$scope.data = result;
console.log("data.name"+$scope.data.name);
});
}
Edit: Regarding Sujoys comment that
What do I need to do so that myFuction() call won't return till .then() function finishes execution.
function myFunction($scope, myService) {
var myDataPromise = myService.getData();
myDataPromise.then(function(result) {
$scope.data = result;
console.log("data.name"+$scope.data.name);
});
console.log("This will get printed before data.name inside then. And I don't want that.");
}
Well, let's suppose the call to getData() took 10 seconds to complete. If the function didn't return anything in that time, it would effectively become normal synchronous code and would hang the browser until it completed.
With the promise returning instantly though, the browser is free to continue on with other code in the meantime. Once the promise resolves/fails, the then() call is triggered. So it makes much more sense this way, even if it might make the flow of your code a bit more complex (complexity is a common problem of async/parallel programming in general after all!)
for people new to this you can also use a callback for example:
In your service:
.factory('DataHandler',function ($http){
var GetRandomArtists = function(data, callback){
$http.post(URL, data).success(function (response) {
callback(response);
});
}
})
In your controller:
DataHandler.GetRandomArtists(3, function(response){
$scope.data.random_artists = response;
});
I was having the same problem and none if these worked for me. Here is what did work though...
app.factory('myService', function($http) {
var data = function (value) {
return $http.get(value);
}
return { data: data }
});
and then the function that uses it is...
vm.search = function(value) {
var recieved_data = myService.data(value);
recieved_data.then(
function(fulfillment){
vm.tags = fulfillment.data;
}, function(){
console.log("Server did not send tag data.");
});
};
The service isn't that necessary but I think its a good practise for extensibility. Most of what you will need for one will for any other, especially when using APIs. Anyway I hope this was helpful.
FYI, this is using Angularfire so it may vary a bit for a different service or other use but should solve the same isse $http has. I had this same issue only solution that fit for me the best was to combine all services/factories into a single promise on the scope. On each route/view that needed these services/etc to be loaded I put any functions that require loaded data inside the controller function i.e. myfunct() and the main app.js on run after auth i put
myservice.$loaded().then(function() {$rootScope.myservice = myservice;});
and in the view I just did
ng-if="myservice" ng-init="somevar=myfunct()"
in the first/parent view element/wrapper so the controller can run everything inside
myfunct()
without worrying about async promises/order/queue issues. I hope that helps someone with the same issues I had.