Use Promises simply to control execution order - angularjs

I'm wondering if this is possible, but I have a series of functions where promises would be a perfect solution for ordering and whatnot, however these functions do not depend on the data from previous promises. I would like to use promises to control execution order but I still need the functions to have access to the scope closure.
Here is some context as to why. On the client I have some object. Various actions cause the client to save/update this object. We recently had a race condition where two actions were done so close together the server actually corrupted the object in the database. I thought it would work well to use a promise chain so update requests will wait until there are no other pending update requests. The update information I need to send to the server obviously exists in the scope so I need to be able to access that when pendingRequestPromise resolves.
I essentially have something that looks like this:
scope.$on("UPDATE", function(event, callback){
$http.post("update", scope.myObj).success(function(updateInfo){
callback(updateInfo);
});
};
If the user does the right actions fast enough, I send 2 objects to the server which caused some problems last week.

I would suggest using the $q service somewhat like this:
//disable all stuff you want to user not to click with ng-disable
$scope.pageLoading=true;
var update1Promise = $http.post("update1", scope.myObj).success(function(updateInfo){
callback(updateInfo);
}),
update2Promise = $http.post("update2", scope.myObj).success(function(updateInfo){
callback(updateInfo);
}),
update3Promise = $http.post("update3", scope.myObj).success(function(updateInfo){
callback(updateInfo);
});
$q.all([update1Promise,update2Promise,update3Promise])then(function() {
$scope.pageLoading=false;
});

Related

What is the right way to chain calls to services in AngularJS?

I need to invoke a Restful service endpoint to get an Id and once I got the result I should invoke a second Restful service endpoint through a angularjs service.
First call returns a customer ID, with this customer ID I need to search for additional info about that customer.
As I should call and and wait for promises I don't think nesting calls to promises is a good pratice. What I mean is that something like the below code isn't supposed to be a neat solution. Am I right or wrong? What should be a good way to achieve this task.
someService.someAction().then( function(data){
var id = data.data;
antoherCallToAnotherService.someOtherAction(id).then(function(data2){
//do some stuff
);
);
Thank you
This is quite typical approach. However, you can optimise it by chaining multiple promises:
someService.someAction().then(function(response) {
var id = response.data;
return antoherCallToAnotherService.someOtherAction(id);
})
.then(function(data2) {
// data is available
});
This is not that bad at all, however can be improved even more if you want. You should probably not make the first promise resolve with entire response object, underlying service should resolve with actual (maybe processed data). Then your code could become:
someService.someAction()
.then(antoherCallToAnotherService.someOtherAction)
.then(function(data) {
// data is available
});
For above to work, someService.someAction should resolve with response.data (which is an id in your case).

angularjs, ngResource in a factory and network calls

Trying to get my head around this. I have a simple factory using ngResouce, like this:
.factory('FooResource', function($resource) {
var foo = $resource('/api/foo').get();
return foo;
})
And in my app, in multiple places, in multiple controllers over time, I use the value of 'FooResource.bar' (where 'bar' is returned in the data from the get() call).
Is it true that the network call to '/api/foo' will only happen on the first reference for the life of my SPA? Does that first reference need to 'FooResource.bar' be handled like a promise?
From what I see in my playing around with code, it seems like the first question is 'yes' and the second is 'no', but don't know if that's really true in general, or just happening because its a small test app on my dev box.
Edit: I guess part of what I want validation on is my thinking that since this is in a factory, which is a singelton, the $resource call will only ever be made once. Is that true?
Depends, and yes. You will always need to handle it as a promise, and you can enable/disable the http cache. If you have the cache set to true, then the request will send off once and be cached until the cache is cleared.
You can find more about the $resource caching in the $resource documentation here: https://docs.angularjs.org/api/ngResource/service/$resource

Call translation service from a callback registered in an app.config section

I'm relatively new to AngularJS and the problem I'm facing is one of those "I want to inject a Service into an app.config" type of scenarios, which I realise cannot be done. (I'm comfortable with the different between Service and Provider, and why a Service cannot be injected into a .config.)
What I am trying to accomplish is to use angular-schema-form together with angular-translate such that field titles in generated forms are translated.
There is an issue where someone asks how to do this, and the advice given is to take advantage of angular-schema-form's postProcess, which is a property of the Provider. This callback gives you the form object before it is rendered, giving you the opportunity to manipulate it with user code. Therefore translation could be done within here.
The postProcess method is called on the Provider, so it is done within an app.config:
app.config(function(schemaFormProvider, $translateProvider) {
schemaFormProvider.postProcess(function(form){
// within here I can inspect the form object, find all
// properties whose key is "title", and then perform
// language translation on their values.
So, that is apparently the place where I have an opportunity to manipulate control titles and so on.
Over to the angular-translate library, for me to 'manually' translate strings, I can use the $translate service. This provides both synchronous and asynchronous methods to translate a given key string. The synchronous one is $translate.instant(key).
To glue these two together, what I have tried so far (which does work) is to create a 'bridge' method like this:
var app = angular.module('myApplicationName', ['schemaForm', 'pascalprecht.translate']);
....
app.config(function(schemaFormProvider, $translateProvider) {
schemaFormProvider.postProcess(function(form){
// ... code here which iterates over properties
// and finds all control titles ...
key = app.myTranslate(key);
// ....
}
....
});
app.myTranslate = function (key) {
var service = angular.injector(['ng', 'myApplicationName']).get("$translate");
return service.instant(key);
}
This does work, but it seems ugly and unsafe (as presumably there's no guarantee $translate is ready when the callback is first invoked) and the calls to angular.injector(['ng', 'myApplicationName']).get... are presumably expensive.
Is there a better way, or is this the only way I'm going to get it done, considering the constraints of the libraries I'm working with?
I have also considered an alternative approach altogether, which would be to instead perform the translations on the schema or form objects before they are processed by angular-schema-form. This could be done from within Controllers, eliminating the problem of accessing the $translate service. I may end up going down that route, but it would still be nice to understand the best solution for the above scenario.

events without $rootScope

Let's say in a service I need to make an http request and store the value, and then if something else uses the same service it could just reuse that stored value, without need of sending a request:
_cachedValue = null
.factory('myService', ($http, $q)->
getFoo: ->
dfrd = $q.defer()
if _cachedValue == null
$http.get('/foo').success (data)->
_cachedValue = data
dfrd.resolve(_cachedValue)
else
dfrd.resolve(_cachedValue)
return dfrd.promise
That kinda works. The problem is - when two consumers simultaneously try to invoke the method, it would send two requests. So I need to send the request only if it never has been sent, and when the second consumer invokes getFoo, it would just wait till the request comes through.
Repeatedly checking every few milliseconds until _cachedValue isn't null - sounds pretty dumb, right? I can't $watch on the value change (there's no $scope inside the service). I can though utilize angular's event bus, and emit an event via $rootScope. But that sounds very overkill, since nobody outside of the service would be interested in listening to that kind of event. Can someone suggest a better way?
You should only set the $http cache property to true.
This will prevent more request if a request is already in progress.
see: https://github.com/angular/angular.js/blob/master/src/ng/http.js#L987

How to use $resource in AngularJS properly for building a client app?

I've been following this tutorial http://draptik.github.io/blog/2013/07/28/restful-crud-with-angularjs/. I implemented a Grails backend with it instead of the Java one in the tutorial.
I've got the data coming back and forth, with one issue. If I create/update/delete a user, I don't see the changes reflected on my user list when I am redirected back. I have to refresh the page to see the updates.
Looking at the network traffic for an edit, it looks like it does a PUT and fires off the GET before the PUT is complete. Assuming this is because $resource returns a promise so things can be done asynchronously. So how do I handle this so that when $location redirects me, my list is up to date?
I'm guessing the options are to wait for the PUT to complete before redirecting/querying for the list, or to somehow manually manage the $scope.users to match the request?
Or maybe this tutorial is just a bad example? Maybe there is a better way to do it (still using $resource)?
Note: I've seen Restangular out there, and I've seen $http with success callbacks, but I would like to understand the situation above.
One way to overcome this issue would be to not redirect to the list page, till you get a callback, and then do a redirect. You can show some busy indicator till that time. The resource call looks like this.
resource.update(config,data,function() { //gets called on success},
function(error) { //gets called on failure});
In real life scenario waiting for the response of update makes sense as you want to handle the error and success scenarios on the same page.
I don't see your code anywhere so i'm just assuming (based on what you wrote and your current problem)
You are probably doing a full (or partial) get each time you changed a user and (re)binding the result to your scope. Doing this in the callback of the resource should actually start the digest cycle angular does to update modified objects. If you had been doing the fetching outside $resource - for example with custom/jquery ajax you would need to execute $scope.$apply()
What i really don't understand you would need to wait for the callback. You already know you added/modified a user. Instead of 'detaching' that user from your scope, modify it, post it to your rest server, then wait for callback, and reinserting it into the scope - why not modify it directly in the list/array you put on your scope?
var users = Users.get(function () {
$scope.users = users.record; // bind the resulting records to the scope
});
$scope.updateUser = function (user) {
resource.update(...); //pseudo
};
Then in your html, you will keep a reference to the currentUser and the div-list will update automaticly.
<div ng-repeat="user in users" ng-click="currentUser=user">{{user.Name}}</div>
<input ng-model="currentUser.Name">
<button ng-click="updateUser(currentUser);">Update</button>
If you don't want to see the update in the list while you type, but only once your callback fires or when you hit the button, would would instead use another ng-model for your input like this:
<input ng-model="tempUser.Name">
And you would then copy the value other in either the updateUser method or in the resource callback like this:
$scope.updateUser = function (user) {
user.Name = $scope.tempUser.Name; // should update automaticly
resource.update(...) // pseudo
}
Hope it helped!

Resources