I'm trying to setup a data service in my Angular application that is using breezeJS. After I resolve my promise I can't get the .then to file in my controller. I am getting data back from my database via breeze in my data service. I could just pass back the breeze promise but I want to be able to use $q.all to know when all my data has been found.
In my controller`
ProApp.controller('caseInfoController',
function caseInfoController($scope, $log, $timeout, caseDataService) {
/***initialize data ***/
// initializeApp();
ATPinitializeApp();
function ATPinitializeApp() {
$scope.MyStateList= caseDataService.getAllStates()
.then(function assignStates(data) {
$log.info("THIS THEN WILL NOT FIRE");
});
}
`
The above then will not fire when the promise from the data service is fulfilled.
ProApp.factory('caseDataService', function ($log, $q)
{
breeze.config.initializeAdapterInstance("modelLibrary", "backingStore", true);
var servicename = "http://localhost:60882/breeze/SPLBreeze";
var manager = new breeze.EntityManager(servicename);
var caseDataService =
{
getAllStates: getAllStates,
};
return caseDataService;
/*** implementation details ***/
function getAllStates()
{
var myStatePromise = $q.defer();
var query = breeze.EntityQuery
.from("state");
manager.executeQuery(query)
.then(function (data) {
$timeout(function () { myStatePromise.resolve(data); }, 200);;
});
return myStatePromise.promise;
};
Any help would be greatly appreciated. I'm not 100% sure if I have the $q promises set up correctly. Eventually I would like to use a $q.all to determine when an array of various promises have been resolved to so I can update a message to the user. I've been reading that I need to use a timeout to get angular to realize that a change has happened in the next event loop.
You're staring down the barrel of an Angular $q bug. You can read about it here if you wish.
That won't help nearly as much as following the advice in my answer to this related StackOverflow question in which I show and describe an adapter to get from Q.js promise to a $q promise.
I have never used breeze but I think your problem is that you are not returning anything on the success callback..
/***initialize data ***/
. . .
function ATPinitializeApp() {
$scope.MyStateList= caseDataService.getAllStates()
.then(function assignStates(data) {
$log.info("THIS THEN WILL NOT FIRE");
return data; // If you donĀ“t return anything nothing will be added to the scope.
});
}
Also the $timeout on the getAllStates function should not be necesary since angular resolves the promises asynchronously (it queues the resolution using $rootScope.$evalAsync)
function getAllStates()
{
. . .
manager.executeQuery(query)
.then(function (data) {
// I believe the $timeout that was in this function is not necessary
myStatePromise.resolve(data);
});
return myStatePromis
e.promise;
}
Hope this could help you a little bit.
Regards,
Carles
Related
This might be a nooby question but I still haven't been able to get my head around promises and specifically how to write code with them. (I've read several articles but most of them are abstract and I simply haven't written enough to have a clear picture)
I've got an AngujlarJS application that gets data through a http request to another server which sends a promise at first. I've been able to retrieve the response from the promise and use it in my app. However because my code is poorly written. It executes other code before the promise is resolved leading to problems. It starts loading the page before it has the data.
what i have is:
var userTotals = *http request which returns a promise
$scope.data = userTotals.$object
//code that does someting with $scope.data
What i need is (I think)
var userTotals = *http request which returns a promise
$scope.data = userTotals.$object.
beforethisresolves(function{
show fancy loading icon or something })
.whenthis resolves(function{
//code that does someting with $scope.data
}
however I can't get the syntax correct.
This is what it looks like in general:
var promise = $http.post('/url');
console.log('Request started');
promise.then(function(result) {
console.log('Success');
console.log(result);
}, function() {
console.log('Failure');
});
In fact, $q AngularJS documentation helped me a good deal to get my head around promises concept.
Hope this helps!
var successCallback = function(){//...};
var errorCallback = function(){//...};
$http
.post('url', data)
.success(successCallback)
.error(errorCallback);
//OR
$http
.post('url', data)
.then(successCallback, errorCallback);
Assuming that you're using Bootstrap modal you can do the following:
function openModalWithProgressIndicator(deferred) {
const progressModal = $uibModal.open({
templateUrl: 'templates/modals/progress.html',
backdrop: 'static'
});
return deferred.then(function (value) {
return value;
}).finally(function () {
progressModal.close();
});
}
The deferred argument passed to this function is a promise. That said you can now do the following:
const deferred = $http.post('http://somewhere/data', {foo: 'bar'});
openModalWithProgressIndicator(deferred)
.then(function (httpResponse) {
// do sth with httpResponse.data
}).catch(function (error) {
// do sth with error
});
So the main point to note is the finally callback that's always executed.
I've been facing a trouble while working with Factory/Service. I've created an AjaxRequests factory for all of my AJAX calls. My factory code is
.factory('AjaxRequests', ['$http', function ($http) {
return {
getCampaignsData: function () {
var campaigns
return $http.get(url).then(function (response) {
campaigns = response.data;
return campaigns;
});
}
}
}])
I've created another service in which I am injecting this factory. My service code
.service('CampaignsService', ['$rootScope', 'AjaxRequests', function ($rootScope, AjaxRequests) {
this.init = function () {
this.camps;
AjaxRequests.getCampaignsData().then(function (response) {
this.camps = response.campaigns;
console.log(this.camps); // It is showing data
})
console.log(this.camps); // But it is not working :(
};
this.init();
}])
And in my controller
.controller('AdvanceSettingsController', ['$scope', 'CampaignsService', function ($scope, CampaignsService) {
$scope.CampaignsService = CampaignsService;
}
])
I've read this article to learn promises but it is not working here. I can directly achieve it in controller and it's been working fine. But it consider as a bad coding standard to make controller thick. But when I use service and factory I stuck. My question is why I am not getting ajax data to use in my whole service ? I need to use CampaignsService.camps in my view template as well as in my whole rest script but every time I get undefined. What is happening here? I've asked the same question before but couldn't get any success. Some one please help me to understand about promises and why I am getting this type of error if I'm working same ? This type of question has already been asked before but it was working in controller. May be I am stuck because I'm using it in a service.
A big thanks in advance.
This is not a bug or some tricky functionality. Just like in any other AJAX implementation, you can only access the response data in AngularJS's $http success method. That's because of the asynchronous nature of Asynchronous JavaScript And XML.
And what you have is working.
.controller('AdvanceSettingsController', ['$scope', 'AjaxRequests', function ($scope, AjaxRequests) {
$scope.camps = [];
AjaxRequests.getCampaignsData().then(function(data) {
$scope.camps = data;
});
}
])
And then bind camps:
<div ng-repeat="camp in camps>{{camp.name}}</div>
What's bad in your implementation is that instead of grouping related stuff in services you are writing a big AjaxRequests service for everything. You should have a CampaignsService that has a getData method and inject that in your controller.
Why is this working? Because $http does a $scope.$apply for you, which triggers a digest cycle after the data is loaded (then) and updates the HTML. So before the then callback that ng-repeat is run with [] and after it it's again run but with data from the response because you are setting $scope.camps = data;.
The reason <div ng-repeat="camp in CampaignsService.camps>{{camp.name}}</div> does not work is because of function variable scoping.
The this reference inside of your then callback is not the same as the this reference outside of it.
This will work and uses the common var self = this trick:
var self = this;
this.camps = [];
this.init = function () {
AjaxRequests.getCampaignsData().then(function (response) {
// here "this" is not the one from outside.
// "self" on the other hand is!
self.camps = response.campaigns;
});
};
This question already has answers here:
Angular Best practice: promise in a Factory or in a Controller?
(3 answers)
Closed 7 years ago.
Pretty new to Angular, what I want to ask is which practice is considered better between
Sending promise to the controller
Controller
factory.method()
.success(function (data) {
// do stuff
})
.error(function (data) {
// throw error
});
Service
return {
method: function() {
return $http.get(url);
}
};
and
Sending data to the controller
Controller
myValue = factory.method();
Service
return $http.get(url).then(function(req){
return req.data;
});
Note that I might be completely off-road here and second method might even not work (haven't tested it yet), but just wanted to ask out of curiosity. If there are better or more effectives practices I would be really glad to hear about them, thank you all very much.
I usually use the first solution that I can confirm it works pretty well. Among other things, for example it allows you to :
control asynchronous flow from your controllers
thus chain services, paralellize and join them, show spinners and stuff
reuse the same services in other asynchronous contexts, such as routes resolution
handle promise rejection
Edit:
That said, in addition, you could do both, like Restangular does with "enhanced promises". Returning a thenable thing, with an $object or $return or whatever attached to it would allow you to :
Use the promise
myService.myMethod.then(
function(data) {//Success
//Manipulate data
$scope.whatever = data;
},
function(err) {
//Handle error
}
);
Or just the $return, that will be populated later, when promise resolves
$scope.whatever = myService.myMethod().$return;
//Notice that you just can't handle rejection
An example of service like that, using angular $q service :
var deferred = $q.defer();
//Create $return reference
deferred.promise.$return = [];
deferred.promise.then(function(data) {
//replace $return elements with data elements, keeping reference
angular.copy(data, deferred.promise.$return);
});
setTimeout(function() {
deferred.resolve([
'White',
'Pinkman'
]);
}, 1000);
return deferred.promise;
Haven't tested this code, but you get the idea
It seems that factory methods execution priority is the highest, so that callbacks has no data to deal with. What is the best way to make this work?
I got this kind of factory
app.factory('jsonService', function($http) {
return {
getDistricts: function(callback) {
$http.get('data/districts.json').success(callback);
},
getLocations: function(path,callback) {
$http.get('data/'+path+'.json').success(callback);
}
};
});
And controller
var app = angular.module('sandbox', []);
app.controller('sandboxCtrl',function ($scope,jsonService) {
//This one works
$scope.init1= function(){
jsonService.getDistricts(function(data){
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
jsonService.getLocations($scope.currentDistrict,function(data){
$scope.locations1 = data;
})
});
};
$scope.init1();
//This one does not
$scope.init2= function(){
jsonService.getDistricts(function(data){
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
})
jsonService.getLocations($scope.currentDistrict,function(data){
$scope.locations1 = data;
});
};
$scope.init2();
});
Here is working plunker
Angular has an implementation of promises named $q (documentation) that you should read up upon.
There is a race condition due to the async nature of http calls. Please review the updated code linked to below that shows an example of your code running (successfully) using promises to handle your two calls in succession.
So upon success of your first call it will call your second service method all without using callbacks thanks to the power of promises.
jsonService.getDistricts()
.success(function(data) {
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
jsonService.getLocations($scope.currentDistrict)
.success(function(locationData) {
$scope.locations = locationData;
})
});
updated PLKR
Promise clarification:
The raw implementation of basic promises uses then to handle responses and promises returned from $http add additional methods (success, error) that will unpack your data from the response object that you would need to handle if your just use then.
init1() is the correct way of doing this. init2() does work because jsonService.getLocations() is getting invoked before jsonService.getDistritcs() completes. The angular $http service is asynchronous. Since jsonService.getLocations() depends on data from jsonServicd.getDistricts() you must wait until .getDistricts() completes before calling .getLocations(). One way to do that is to call .getLocations() within the .getDitricts() callback, just as you did in init1().
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.