Update field when function finishes its calculations - javascript

I got this function that calls an external service, works the results and then returns a single variable.
I want this variable to be inserted into an input but I'm doing something wrong cause instead of updating the form at the end of the function (as expected) I have to call the function a second time and the code updates the field with the value of the FIRST execution...
This is the controller:
$scope.cercaClienteNomeCognome = function() {
if ($scope.nome == undefined){
var name = "";
} else name = angular.uppercase($scope.nome);
if ($scope.cognome == undefined){
var surname = "";
} else surname = angular.uppercase($scope.cognome);
var url = "servizi/getClienteNomeCognome?nomeCliente="+name+"&cognomeCliente="+surname;
esitoRicercaEstesa = TreeService.avviaRicercaEstesa(url);
if (esitoRicercaEstesa == "true") {
vm.cercaSecondario = TreeService.getProClie(); // THIS_LINE
}
};
vm.cercaSecondario is the field that I'm trying to update.
This is the service:
service.avviaRicercaEstesa = function(url) {
service.url = url;
$http.get(url)
.success(function(data, status, headers, config) {
service.apriModaleEstensioneRicerca(data);
})
.error(function(data, status, headers, config) {
toaster.error("[SERVIZIO RECUPERO CLIENTI] Errore durante il ritrovamento dei clienti");
});
}
service.setProClie = function (pro_clie) {
service.pro_clie = pro_clie;
}
service.getProClie = function () {
return service.pro_clie;
}
Example: I run the code for the FIRST time. Everything goes fine.
When I get to THIS_LINE the field doesn't update.
I then run the code for the SECOND time and when I get to THIS_LINE the field updates with the value from the FIRST execution......
What am I doing wrong?!
I've even tried using $timeout and $evalASync but with no success...

Maybe your service caused this problem. Try to use $q and return promise from your service. Then use it like this
$scope.cercaClienteNomeCognome = function() {
if ($scope.nome == undefined){
var name = "";
} else name = angular.uppercase($scope.nome);
if ($scope.cognome == undefined){
var surname = "";
} else surname = angular.uppercase($scope.cognome);
var url = "servizi/getClienteNomeCognome?nomeCliente="+name+"&cognomeCliente="+surname;
esitoRicercaEstesa = TreeService.avviaRicercaEstesa(url);
if (esitoRicercaEstesa == "true") {
TreeService.getProClie().then(function (cercaSecondario) {
vm.cercaSecondario = cercaSecondario;
});
}
};

My bet would be that your remote call will make your code to be "out of angular context" so what happens is that by the time you get your response from your remote call angular won't know this and won't digest. So you either need to manually call $scope.$digest() after vm.cercaSecondario = TreeService.getProClie(); or you can wrap your remote call into $q.when(remoteCall).then(...). This way angular will digest by it's own.

Try like this:
TreeService.getProClie()
.then(function(res){
vm.cercaSecondario = res;
})
EDIT:
This to work you have to set your service to return promise.
.service('TreeService', function($q){
var data = this;
data.getProClie = function(){
var defer = $q.defer();
$http.get('example/url')
.success(function(res){
defer.resolve(res)
})
.error(function(error,status){
defer.reject(error);
})
return defer.promise;
};
})

write getProClie function in this way.
You need to promise chaining
service.getProClie = function () {
var defer = $q.defer();
defer.resolve(service.pro_clie);
return defer.promise;
}
Then in controller :
TreeService.getProClie().then(function(data){
vm.cercaSecondario = data
},
function(){
//error handling
});

Related

AngularJs $filter in service using function

I got stuck with learning angular in particular the $filter('filter') function and how to pass a local function to it as a filter function.
The API (json files) contain all the trips in trips.json and the id array of all the trips the user has been on in the user.trips.json.
The factory getting fetching the data looks like this:
app.factory("tripsApi", function ($http) {
return {
allTrips: $http.get('/api/trips.json')
.success(function (response) {
return response;
})
.error(function (error) {
return error;
}),
userTrips: $http.get('api/user.trips.json')
.success(function (response) {
return response;
})
.error(function (error) {
return error;
})
}
});
The next piece of code is just a service to retrieve all the user trips information. The service uses a factory to access the API (in this case just the men json files). And it SHOULD filter through the trips information to retrieve only the ones the user has been on using the id array.
app.service("trips", function (tripsApi, $filter) {
var self = this;
this.user = {
trips: Array()
};
this.trips = Array();
this.getUserTrips = function () {
self.getAllTrips.then(function () {
tripsApi.userTrips.success(function (response) {
self.user.trips = $filter('filter')
(self.trips, self.containsChild(id, response));
});
});
};
this.getAllTrips = tripsApi.allTrips.success(function (response) {
self.trips = response;
});
this.containsChild = function (id, idsArray) {
if (id != 0 && idsArray != null) {
for (var i = 0; i < idsArray.length(); i++) {
if (idsArray[i] == i)
return true;
return false;
}
}
}
});
Yet I can't get it to work. The first error I get is the id not defined in the self.containsChild(id, response);
Where are the mistakes? Any help is welcome :)
The id issue, the call should be:
$filter('filter')(self.trips, self.containsChild); // this won’t really work, though, because your `containsChild` function is not properly defined.
Second, This is not a properly defined service. You want something that looks like the following
app.service('ServiceName', [ '$filter', function ($filter) {
var myInstance = {};
myInstance.containsChild = function (value, index, array) {
};
myInstance.user = . . .;
return myInstance;
}]);
Third, fix your containsChild function to take three parameters. The first will be the value passed to it, the second will be the index, and the third will be the array being filtered.
Yet I can't get it to work. The first error I get is the id not
defined in the self.containsChild(id, response);
Where are the mistakes?
You passed id to the function but there's no idvariable declared anywhere in the code the you provided which is what's causing the error.
Also, I've observed that there's no consistency in your code, you assigned this to self but you keep using this and self everywhere in your code.

angular call http request to the function ng-repeat return

I would like to call a function in mg-repeat and this function call a http request with id for find list data but when i try this i have one error message this.
this a call of function :
<div ng-repeat="ListeReponse in reponsefonction(Listechamps.keyQuestion)" >
<label class="col-xs-4 control-label">
{{ListeReponse.reponse}}
</label>
</div>
this is a function :
$scope.reponsefonction = function(idQuestion)
{
var defer = $q.defer();
return RequestService.get('question/'+idQuestion+'/reponse').success(function(data)
{
defer.resolve(data);
})
return defer.promise;
}
My Service :
app.factory('RequestService', function ($http, WEB_SERVICE_URL)
{
var requestService = {};
requestService.get = function (type)
{
var response = $http.get(WEB_SERVICE_URL.url+type);
return response;
};
// ajout
requestService.post = function (type, data)
{
var response = $http.post(WEB_SERVICE_URL.url+type, data);
return response;
};
requestService.put = function (type, data)
{
var response = $http.put(WEB_SERVICE_URL.url+type, data);
return response;
};
return requestService;
})
error message :
docs.angularjs.org/error/$rootScope/infdig?p0=10&p1=%5B%5D
You created an infitie loop. Each call will trigger another call. Angular evaluates the responseFunction everytime the calls return. You will need to re-work your app a little:
Do not call a function in ng-repeat. Instead simply link it a to a variable. Fill the variable once on startup with a get query.
I don't think ng-repeat works with promises. In this case the ng-repeat is looping through "null/undefined" which causes this error.
Try this:
html:
<div ng-repeat="ListeReponse in getResponses(Listechamps.keyQuestion)" >
<label class="col-xs-4 control-label">{{ListeReponse.reponse}}</label></div>
controller:
var idQuestions = {}; // keep track of the entire list
$scope.getResponses = function(idQuestion) {
var notInitialized = idQuestions[idQuestion] == null;
idQuestions[idQuestion] = notInitialized ? [] : idQuestions[idQuestion]; // initialize to empty array
if (notInitialized) {
$scope.reponsefonction(idQuestion); //kicks off ajax request to get data
}
return idQuestions[idQuestion];
}
$scope.reponsefonction = function(idQuestion) {
RequestService.get('question/' + idQuestion + '/reponse').success(function(data) {
//assuming data is an []
Array.prototype.push.apply(idQuestions[idQuestion], data); //update the same array object to prevent multiple $digest cycles
}
}
}

Updating Json values with a promise

I want to populate some values in Json that are being calculated with angular-promises and these value should be updated after certain events.
I tried to call the factory which yields the values for example something like below and tried to call the functions GetWeeklyVal and GetDailyVal which are in charge of calculating the values :
this.salesList =
{"sales":[
{ "id":"A1", "dailyValue": GetDailyVal('A1'), "weeklyValue": GetWeeklyVal('A1')},
{ "id":"A2", "dailyValue": GetDailyVal('A2'), "weeklyValue": GetWeeklyVal('A2')}
]}
and in my controller I have:
$scope.sales= salesServices.salesList.sales;
but it didn't work. the values remain zero which is the default value in the application.
Why the values are not being updated and what would be a better solution?
update
This is the portion of the code I call the calculation functions: (I skip the portion to get the values based on passed id in here)
function GetDailyVal(id){
var dValue = 0;
salesService.getSales();
dValue = salesService.totalAmount;
return dValue;
}
this is the factory
.factory('salesService', ['$http', '$q'],
function salesInvoiceService($http, $q) {
var service = {
sales: [],
getSales: getSales,
totalAmount: 0
};
return service;
function getSales() {
var def = $q.defer();
var url = "http://fooAPI/salesinvoice/SalesInvoices"; //+ OrderDate filter
$http.get(url)
.success(function(data) {
service.sales = data.d.results;
setTotalAmount(service.sales);
def.resolve(service.sales);
})
.error(function(error){
def.reject("Failed to get sales");
})
.finally(function() {
return def.promise;
});
}
function setTotalAmount(sales){
var sum = 0;
sales.forEach(function (invoice){
sum += invoice.AmountDC;
});
service.totalAmount = sum;
}
})
I think there are some errors in your code.
I give some sample code here. I think this will help you.
This is a sample code in one of my application. Check it.
service.factory('Settings', ['$http','$q', function($http,$q) {
return {
AcademicYearDetails : function(Details) {
return $http.post('/api/academic-year-setting', Details)
.then(function(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
return $q.reject(response.data);
}
}, function(response) {
return $q.reject(response.data);
});
},
newUser : function(details) {
return $http.post('/api/new-user', details);
}
}
}]);
The reason why its not working is:
dailyValue: GetDailyVal('A1')
Here, GetDailyVal makes an async ajax call to an api. For handling async requests, you have to return a promise as follows in your GetDailyVal function as follows:
function GetDailyVal() {
salesService.getSales().then(function(data) { //promise
dValue = salesService.totalAmount;
return dValue;
})
}
Same thing need to be done for weeklyValue.

Angularjs Factory deferred's data disapearing

I'm trying to do a caching factory for http requests, so it doesn't make the server do a lot of work for the same request. But It seems my way of using deferred "swallows" the data, and I don't know why.
Console output for below:
data fetched:
Object {state: "OK", data: Object, errorMessage: null, exception: null}
success
undefined
ImportFactory:
factory("importFactory", function ($http, $q, loggingService) {
return{
fetchedData: [],
cacheTransport: function (transportsId, data) {
this.fetchedData.push({"transportsId": transportsId, "data": data});
},
getImport: function (transportsId) {
var factory = this;
var deferred = $q.defer();
var preFetchedTransport = this.findTransport(transportsId);
if (preFetchedTransport === null) {
console.log('fetching from backend');
return $http.post("/import/create/" + transportsId).then(function (data) {
console.log('data fetched:');
console.log(data);
factory.cacheTransport(transportsId, data);
deferred.resolve(data);
});
}
preFetchedTransport = deferred.promise;
return preFetchedTransport;
},
findTransport: function (transportsId) {
for (var i = 0; i < this.fetchedData.length; i++) {
var transportObj = this.fetchedData[i];
if (transportObj.transportsId === transportsId) {
return transportObj.data;
}
}
return null;
}
};
});
Controller
.controller('ImportController', function ($scope, $routeParams, importFactory){
$scope.transportId = $routeParams.id;
importFactory.getImport($scope.transportId).then(function (successData) {
console.log('success');
console.log(successData);
}, function (errorData) {
console.log('error');
console.log(errorData);
});
You basically need this: Demo here.
var cachedPromises = {};
return {
getStuff: function(id) {
if (!cachedPromises[id]) {
cachedPromises[id] = $http.post("/import/create/" + id).then(function(resp) {
return resp.data;
});
}
return cachedPromises[id];
}
};
Now, when you fetch that data, you can manipulate and it will be changed when you access it in the future.
myService.getStuff(whatever).then(function(data) {
data.foo = 'abc';
});
//elsewhere
myService.getStuff(whatever).then(function(data) {
console.log(data.foo); // 'abc'
});
Here's a demo that does this, as well as a view updating trick (bind the object to the view before the data comes in), and an idea of how you could change the data separately from the cache, in case you want to have the original data and the changing data. http://jsbin.com/notawo/2/edit
Remember to avoid that nasty promise anti-pattern. If you already have a promise, use that instead of creating another with $q. $http already returns a promise and that promise is sufficient for whatever you need if you use it properly.
just change the loop condition look like this and then test i think your function and defer is work fine but the loop does not sent the correct data
for(var i = 0; i < this.fetchedData.length; i++) {
if (this.fetchedData[i].transportsId === transportsId) {
return this.fetchedData[i].data;
}
}
return null;
}
The reason you are getting undefined is you are not returning anything from the $http.post().then() !
Also in your getImport() function you are returning an empty promise when the transport is already cached. You need to resolve it to your already cached transport object.
getImport: function (transportsId) {
var factory = this;
var deferred = $q.defer();
var preFetchedTransport = this.findTransport(transportsId);
if (preFetchedTransport === null) {
console.log('fetching from backend');
return $http.post("/import/create/" + transportsId).then(function (data) {
console.log('data fetched:');
console.log(data);
factory.cacheTransport(transportsId, data);
return data; //this was missing
});
}
// resolve it with transport object if cached
deferred.resolve(preFetchedTransport);
return deferred.promise;
},

Dojo using deferred functions to get data in ajax callback function

I have a function with a return however in the function there is an async request which holds the value that is suppose to be returned by the function. I understand with the nature of async request the function will complete and not return a value while waiting on the async function to complete.
I attempted to use dojo deferred functions to have my function PostInformation() to return a value within the ajax request callback. I am having some issues and i am not sure where my issue is. Under is my code:
Dojo Deferred Function
function PostInformation(){
var hasErrors = false;
var containers = [dijit.byId("container1"), dijit.byId("container2")];
var Employee = {
//data
};
var def = new dojo.Deferred();
def = dojo.xhrPost({
url: 'hello',
content: Employee,
load: function (data) {
formErrors = {
"errors": true,
"fName": "123",
"surname": "456",
"oNames": "789",
"bSurname": "784585"
};
//formErrors = (JSON.parse(data)).formErrors;
$.each(formErrors, function (key, value) {
if (key == 'errors') {
hasErrors = value;
//console.log('hasErrors set to '+value);
}
});
if (hasErrors == true) {
for (var i = 0; i < containers.length; i++) {
var processingContainer = containers[i];
dojo.forEach(processingContainer.getChildren(), function (wid) {
var widgetName = wid.attr('id');
$.each(formErrors, function (key, value) {
if (key == widgetName && value.length > 0) {
var myWidget = dijit.byId(widgetName);
//var wdgName = dijit.byId(widgetName).attr("id");
var myWidgetValue = value;
myWidget.validator = function () {
//console.log('Attribute Name is :' + wdgName + ' Error Value is : ' + myWidgetValue);
//console.log(wdgName + " : "+myWidgetValue);
this.set("invalidMessage", myWidgetValue);
};
myWidget._hasBeenBlurred = true;
myWidget.validate();
}
});
});
}
}
console.log(hasErrors);
def.resolve(hasErrors);
},
error: function(err){
console.log(err);
def.reject(err);
}
});
def.then(function(data){
console.log('In the then function');
//alert('In the def.then and the results is : ' + data);
if(data == true){
return false;
}else{return true;}
},function(err){
return false;
alert('In the def.error and there has been an error ' + err);
});
//return the value of hasErrors here
};
Devdar, you are making heavy wether out of something quite simple. In particular, you don't need to loop through an object to access one of its properties, and the variable hasErrors is not really necessary.
Your code should simplify to something like this :
function PostInformation() {
var $containers = $("#container1, #container2");
var Employee = {
//data
};
return dojo.xhrPost({
url: 'hello',
content: Employee
}).then(function(data) {
data = JSON.parse(data);
var formErrors = data.formErrors;
if(formErrors.errors) {
$containers.each(function(i, c) {
$(c).children().each(function(wid) {
var val = formErrors[wid.id],
myWidget;
if(val) {
myWidget = dijit.byId(wid.id);
myWidget.validator = function() {
this.set("invalidMessage", val);
};
myWidget._hasBeenBlurred = true;
myWidget.validate();
}
});
});
//Send an enhanced error object down the "error" route
throw $.extend(formErrors, {
'message': 'PostInformation(): validation failure'
});
}
//Send the data object down the "success" route
return data;
});
};
PostInformation().then(function(data) {
console.log('PostInformation(): everything went OK');
//access/process `data` here if necessary
//and/or just display a nice "success" message to the user
}, function(err) {
console.error(err.message);
});
Barring mistakes on my part, this code should do everything you want and more. As with your own code, it processes the server's JSON response and returns a Promise, but that's where the similarity stops.
In your code, you seek to return a Promise which is eventually resolved with a boolean to indicate whether or not errors were detected. Whilst this will (if correctly written) meet your immediate needs, it is not the best Promise logic.
In my code, the Promise is resolved only if validation succeeds and rejected if validation fails for whatever reason. Not only is this logically correct behaviour for a Promise (success goes down the success route, and errors go down the error route) but as a bonus should (see note below) also allow you to pass more information to whetever function(s) eventually handle errors. I choose to pass the whole formErrors object enhanced with an error message, thus providing a great deal of freedom in the error handler to display/log/etc as much or as little as is appropriate, and with virtually no assumption inside PostInformation() as to what will happen subsequently. You currently believe that you will only read and act on the boolean formErrors.errors but it could be beneficial to pass as much error data as possible thus allowing yourself the freedom to change your mind at a later date without needing to change anything in PostInformation().
In this regard you can think of PostInformation() as an agent of the server-side service; and like that service, it can be written with incomplete knowledge (or maybe no knowledge at all) of how the (promise of) data/errors it delivers will be used by "consumer code".
NOTE: I have to admit that I'm not 100% familiar with Dojo's Promises, so I'm not sure that a JS plain object can be thrown in the way I indicate. I have found evidence but not proof that it can. For that reason, I am cautious above in saying "your code should simplify to something like this" Anyway, that issue aside, the principle of sending success down the success route and errors down the error route should still apply.
I'd suggest this where you create your own Deferred() object, return it from your PostInformation() function and then register .then() handlers on it so you can pick up the resolve or reject on your own Deferred object that happens inside the PostInformation() function.
The way you had it you were creating your own Deferred() object, but then immediately overwriting it with the xhrPost return result which meant def is now something else and you weren't returning your Deferred from PostInformation() so it can be used outside that function to track the progress.
function PostInformation() {
var hasErrors = false;
var containers = [dijit.byId("container1"), dijit.byId("container2")];
var Employee = {
//data
};
var def = new dojo.Deferred();
dojo.xhrPost({
url: 'hello',
content: Employee,
load: function (data) {
formErrors = {
"errors": true,
"fName": "123",
"surname": "456",
"oNames": "789",
"bSurname": "784585"
};
//formErrors = (JSON.parse(data)).formErrors;
$.each(formErrors, function (key, value) {
if (key == 'errors') {
hasErrors = value;
//console.log('hasErrors set to '+value);
}
});
if (hasErrors == true) {
for (var i = 0; i < containers.length; i++) {
var processingContainer = containers[i];
dojo.forEach(processingContainer.getChildren(), function (wid) {
var widgetName = wid.attr('id');
$.each(formErrors, function (key, value) {
if (key == widgetName && value.length > 0) {
var myWidget = dijit.byId(widgetName);
//var wdgName = dijit.byId(widgetName).attr("id");
var myWidgetValue = value;
myWidget.validator = function () {
//console.log('Attribute Name is :' + wdgName + ' Error Value is : ' + myWidgetValue);
//console.log(wdgName + " : "+myWidgetValue);
this.set("invalidMessage", myWidgetValue);
};
myWidget._hasBeenBlurred = true;
myWidget.validate();
}
});
});
}
}
console.log(hasErrors);
def.resolve(hasErrors);
},
error: function (err) {
console.log(err);
def.reject(err);
}
});
return def.promise;
};
PostInformation().then(function (data) {
console.log('In the then function');
// process data value here which will contain the value you resolved with
}, function(err)
// process an error in the ajax result here
});
I think this is more of an issue with design of the function then.
Since the xHR call is asynchronous, the postInformation shouldn't really return anything unless it's the Deferred object itself. An alternative option is to have postInformation do some sort of event publishing (dojo/topic), that other functions will subscribe to and know how to handle said events.

Categories

Resources