I am trying to build an AngularJS single page application with Java Spring as rest service.
Currently my controller allows me to save all the filled data to the back end which I have something like this in my code:
$scope.$on('$destroy', function () {
personalDataService.saveData.query({}, {
personalData: $scope.personalData
}, function (res) {
console.log(res);
});
});
In another page, I loaded the part of the data from the same table in order to do some validation, for example checking if user is female to determine she needs to fill another form.
The problem is while I am leaving this page, the promise for save is not return yet, the second page has been loaded and the data is not completed. If I refresh the second page again then everything is back to normal
Is there any way to make sure all the promise has been return before destroy/ leaving this page?
Once the promise is resolved, emit a event.
function saveFields() {
var deferred = $q.defer();
// in your save fields function, resolve the promise on success save or reject the promise on error.
onSaveCall(args, onSuccess, onFail);
var onSucess = function() {deferred.resolve()};
return deferred.promise;
}
$scope.$on('destroy', function () {
onSaveCall.then(function () {
// Here your save fields is resolved then write your code to emit or notify.
})
})
If you have more than one promise use promises.all https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Build an array of all your promises running (for example, by storing them in a service, in the $rootScope or via events) and wait for the completion by using $q.all():
$scope.$on('$destroy', function () {
$q.all($rootScope.promises).then(function(){
console.log('all promises finished');
});
}
Related
I am newbie on angularjs, i have some problems about promise using with ng filling.
I want to access filled html data with javascript. But the data on the page can be change dynamically.
When click some button. It should be fill angular with changable data. Then will take the recent filled html source.
array.forEach( function (id) {
var promise = $http.post('http://postpagedomain.aspx?id=' + id).then(onComplete, onError);
promise.then(function () {
var html = $('#divframe').html();
}
);
});
var onComplete = function (response) {
$scope.Info = response.data;
}
Bu it's not taking recent data cause of synchronous problem. How can i handle it?
Instead of taking response of your http call in a variable, directly make http call. Something like:
$http.post('http://postpagedomain.aspx?id=' + id).then(onComplete, onError);
So, this will call the onComplete method when API returns success and write all your logic of actions to be performed once API call gets success in the function body of onComplete.
Also include a function of onError in your code to get any exceptions thrown by the API.
Your code can look something like:
array.forEach( function (id) {
$http.post('http://postpagedomain.aspx?id=' + id).then(onComplete, onError);
});
function onComplete(response) {
$scope.Info = response.data;}
I have a open modal in controller called "Audit"
$scope.comment = function (spec){
ManualService.setManual(spec);
var modalInstance = $modal.open({
templateUrl: 'views/comment.html',
controller: 'CommentCtrl',
size: 'lg'
});
}
When the users clicks on comment button the modal opens and user can add comments. On closing the comment modal i'm trying to update the modal in "Audit" controller but it's not happening
Below function is in a different controller called "Comments"
$scope.cancel = function () {
$http.post('/api/v1/manual/manual',$scope.id).success (function (data) {
$scope.manual = data;
ManualService.setManual($scope.manual);
}).error(function (data, status) {
console.log('Error ' + data);
});
$modalInstance.dismiss('cancel');
};
My question is how do I return the new data I got from calling the endpoint on cancel function without reloading the page
Return the promise returned by the $http service:
$scope.cancel = function () {
var promise = $http.post('/api/v1/manual/manual',$scope.id)
.then (function (response) {
var manual = response.data;
return manual;
});
$modalInstance.dismiss(promise);
};
Then chain from the result:
$modal.open({}).result.then(
function onClose(value) {
// Use the value you passed from the $modalInstance.close() call
},
function onCancel(manual) {
ManualService.setManual(manual);
$scope.manual = manual;
}
)
The $modal service creates a $scope that is destroyed when the modal closes. Use the promise returned by the modal service to update the proper $scope.
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.
— AngularJS $q Service API Reference - Chaining Promises
See also UI Bootstrap Modal DEMO and API Reference
The $modal.open() function returns a few things, and you're looking for the result property. Here's the documentation, take a look at the result property.
The result property returns a promise that you can chain on in order to do certain things when the modal is either "closed" or "dismissed".
When closing a modal, you have two options:
$modalInstance.close(value)
$modalInstance.dismiss(value)
You can use either one, but I'd suggest using the close() function for "successful" completion, and dismiss() for "cancelled" or "failed" modal operations. Typically the "x" button in the top right of the modal will call the dismiss() function, so you can handle dismissal separately from completion.
So, you'll end up with something like this:
$modal.open({}).result.then(
function (value) {
// Use the value you passed from the $modalInstance.close() call
},
function (dismissed) {
// Use the value you passed from the $modalInstance.dismiss() call
}
)
I want to use the library MIDI.js in my AngularJS app. To initialise it, you call MIDI.loadPlugin which takes a callback that fires when some files are loaded.
I want to make a controller in my app aware of when the files are loaded.
My current approach is a dedicated service which calls MIDI.loadPlugin when it is created, and sends a $rootScope.$broadcast when the callback is fired.
Is this the best approach, particularly in regards to testability?
I would use a promise in your service to get access to the async resource. Then for all functions in the service that need it they just call the get method that returns a promise.
I'll try to post some example code later when on the computer.
Here's the solution I came up with based on Gordon's suggestion.
.factory('midiLoader', ['MIDI', '$q', function (MIDI, $q) {
var service = {};
service.loadFiles = function() {
return $q(function(resolve, reject) {
MIDI.loadPlugin({
soundfontUrl: "bower_components/midi/soundfont/",
instrument: "acoustic_grand_piano",
callback: function () {
resolve(null);
}
});
});
}
return service;
});
I have a service in Angular that fetches a list of querys from a database table. The user selects one and moves on to another view to work with it. When the user clicks back to the first view though, I'd like to avoid re-fetching the query list. The query list is stored in the service but I'm having a hard time handing it back to the controller given that my fetch routine uses .then and promises.
app.service('queryService', function ($http)
{
var querys = new Object();
this.loadQueryList = function()
{
if (querys!=null)
{
//how to return the querys list here? caller expecting a promise
}
var promise = $http.post("php/datapump.php", { action: "FETCH", item: "QUERYS", id1: null, id2: null})
.then(function (response)
{
querys=response.data;
return querys;
});
return promise; // Return the promise to the controller
};
});
In the controller initialization the service is called with:
queryService.loadQueryList().then(function(d)
{
$scope.querys = d;
$scope.selectedquery=queryService.getSelectedQuery();
});
Or is there a better method altogether? I can imagine setting some flag in the service to get around this but it seems ugly. I simply want to avoid fetching the data a 2nd time.
You could create your own promise and resolve it immediately. This is much nicer than setting a flag and returning some cached data directly because it keeps your service interface consistent - that method always returns a promise.
var deferred = $q.defer();
deferred.resolve(yourdata);
return deferred.promise;
You will obviously need to inject $q into your service.
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.