I want to modify this vtiger function that does a few checks before submission.
I added globalvarwhich i want to use to check if the form can be submitted. What can I change here to get the required results?
globalvar should be 2 or 3 by the end of the function but it stays 1, i also added alerts where its being set to make sure that it gets to those points.
registerRecordPreSaveEvent : function(form) {
var thisInstance = this;
if(typeof form == 'undefined') {
form = this.getForm();
}
form.on(Vtiger_Edit_Js.recordPreSave, function(e, data) {
var accountName = thisInstance.getAccountName(form);
var recordId = thisInstance.getRecordId(form);
globalvar = 1;
var params = {};
if(!(accountName in thisInstance.duplicateCheckCache)) {
Vtiger_Helper_Js.checkDuplicateName({
'accountName' : accountName,
'recordId' : recordId,
'moduleName' : 'Accounts'
}).then(
function(data){
thisInstance.duplicateCheckCache[accountName+'_accounts'] = data['success'];
globalvar =2;
},
function(data, err){
thisInstance.duplicateCheckCache[accountName+'_accounts'] = data['success'];
thisInstance.duplicateCheckCache['message'] = data['message'];
var message = 'Company already exists';
Vtiger_Helper_Js.showMessage({'text' : message,'type': 'error'})
}
);
}
else {
if(thisInstance.duplicateCheckCache[accountName+'_accounts'] == true){
var message = 'Company already exists';
Vtiger_Helper_Js.showMessage({'text' : message,'type': 'error'})
} else {
delete thisInstance.duplicateCheckCache[accountName+'_accounts'];
return true;
}
}
if(!(accountName in thisInstance.duplicateCheckCache)) {
Vtiger_Helper_Js.checkDuplicateName({
'accountName' : accountName,
'recordId' : recordId,
'moduleName' : 'Leads'
}).then(
function(data){
globalvar =3;
console.log(globalvar);
thisInstance.duplicateCheckCache[accountName+'_leads'] = data['success'];
},
function(data, err){
thisInstance.duplicateCheckCache[accountName+'_leads'] = data['success'];
thisInstance.duplicateCheckCache['message'] = data['message'];
var message = 'Lead already exists';
Vtiger_Helper_Js.showMessage({'text' : message,'type': 'error'})
}
);
}
else {
if(thisInstance.duplicateCheckCache[accountName+'_leads'] == true){
var message = 'Lead already exists';
Vtiger_Helper_Js.showMessage({'text' : message,'type': 'error'})
} else {
delete thisInstance.duplicateCheckCache[accountName+'_leads'];
return true;
}
}
console.log(globalvar);
e.preventDefault();
})
}
This code has numerous asynchronous operations in it. An asynchronous operation completes sometime later, after the containing function has actually returned. Thus, you cannot use the results of an asynchronous operation immediately after the containing function has finished because the asynchronous operation itself is still going and has not completed.
So, setting globalVar in an async operation (which you are doing) will not show it's value when the containing function has completed.
The only way to use the results of an async operation is to use it IN the callback that signifies the async operation is complete or to call a function from that callback and pass it the data you want. Those are your choices. You simply can't do it the way you're trying to do it.
As with all questions here, if you describe the actual problem you were trying to solve rather than explain the problem with your attempted solution, we could probably be helpful at a much higher level.
On StackOverflow, the failure to describe what you're really trying to accomplish is known as the XY problem.
You cannot run e.preventDefault() in an asynchronous response because by the time that async response finishes, the form has already been submitted.
You will likely have to flip your logic so that the form is NOT submitted until all async operations have completed with no errors. Then, you will manually submit the form.
Related
In my application , a ODATA rest service is called which takes long time to execute and return response. This causes the browser to start showing Wait / Kill alert popup again and again till response finally comes.
this.getView().setBusy(true); //start showing busy indicator
.
.
.
setTimeout(function() {
oModel.create('/serviceURL?sap-client='
+ sapClient, requestObj, null, function(
data, oev) {
// Success function
console.log('fffff');
that.getView().setBusy(false);
if (data.JsonOut.indexOf("message:") != -1) {
var arr = data.JsonOut.split("message:");
sap.m.MessageBox
.alert(arr[1].split(",")[0]);
}
that.onBindTable();
}, function(err) {
// Error function
that.getView().setBusy(false);
var errorObj = JSON.parse(err.response.body);
var sMsgs = errorObj.error.message.value;
sap.m.MessageBox.alert(sMsgs);
});
}, 2000);
.
.
// rest of code gets executed
.
.
I have enclosed my request inside setTimeOut() function to make the call asynchronous but still problem persists. How to avoid popup message from showing up ?
i suggest you to set the fifth param of the oModel.create method to 'true' which will cause the call to be asynchronous (and remove the setTimeOut()).
oModel.create('/serviceURL?sap-client='
+ sapClient, requestObj, null, function(
data, oev) {
// Success function
console.log('fffff');
that.getView().setBusy(false);
if (data.JsonOut.indexOf("message:") != -1) {
var arr = data.JsonOut.split("message:");
sap.m.MessageBox
.alert(arr[1].split(",")[0]);
}
that.onBindTable();
}, function(err) {
// Error function
that.getView().setBusy(false);
var errorObj = JSON.parse(err.response.body);
var sMsgs = errorObj.error.message.value;
sap.m.MessageBox.alert(sMsgs);
}, true);
Find a hint there, not sure if it's revelent as i couldn't find the javascript API reference.
https://archive.sap.com/discussions/thread/3503659
This is my first time here, and I am really lost on what to do.
So I have a loop where I am making a post request to an API. I need to send some questions and get their rates match back. After that, I need to calculate the highest match and set an answer in some $scope variable to display on the screen.
The problem I have is that, since the call is asynchronous, sometimes it will display the wrong answer (because it's the last response returned).
I know it's asynchronous and I should have some callback, but I tried many ways and still can't figure out how to. All I want is to be able to "sort" the result so I can get the max number and display the associated answer AFTER all the calls are done. What I have so far is that:
for (var i = 0; i < $scope.possibleQuestions.length; i++) {
var compare = compareAPI($scope.results, $scope.possibleQuestions[i].question,
$scope.possibleQuestions[i].answer,
$scope.possibleQuestions[i].keywords,
$scope.possibleQuestions[i].similar,
function (score, question, answer, keywords, similar) {
$scope.compareAPI = score.average;
if ($scope.compareAPI >= 0.6) {
realAnswer = answer;
questionsAskedCount++;
$scope.answer = realAnswer;
} else {
var isMatch = matchKeywordAPI(question, keywords);
if (isMatch == 0) {
$scope.answer = "Please try asking another question!";
}
else {
//have to return a string just to know, bcause realAnswer is undefined in here, have to return callback function hahahahaha again, or set the answer in the match function
$scope.answer = realAnswer;
}
}
});
}
And the other function:
function compareAPI (possibleQuestion, possibleAnswer, fn) {
console.log("compare API");
var apiMatch = semanticService.getSemantic($scope.studentQuestion, possibleQuestion)
apiMatch.then(function (result) {
fn(result.data, possibleQuestion, possibleAnswer);
console.log(result.data);
}, function(error){
$scope.status = 'Unable to load question data: ' + error.message;
});
}
My biggest problem is that this part
if ($scope.compareAPI >= 0.6) {
realAnswer = answer;
questionsAskedCount++;
$scope.answer = realAnswer;
} else {
var isMatch = matchKeywordAPI(question, keywords);
if (isMatch == 0) {
$scope.answer = "Please try asking another question!";
}
else {
$scope.answer = realAnswer;
}
}
is random because of the async, so if the wrong answer is the last response, it will go to the 'else' and the answer gets wrong.
Any help will be very appreciated!!! Thanks!
Promises can done sequentially by chaining them:
var promise = $q.when(null);
for (let i=0; i<promiseArray.length; i++) {
promise = promise.then(function() {
//return promise to chain
return promiseArray[i];
});
};
promise.then(function() {
console.log("ALL Promises Done");
});
For more information, see AngularJS $q Service API Reference - Chaining Promises.
I have 2 different loops that perform POST and DELETE operations respectively.
Once, the all the operations are complete, I want to make another service request.
my issue is:
In case of error on delete operation, the control goes to the catch block and a dialog confirmation is needed to decide if the item still needs to be deleted.
If a confirmation comes from the dialog, I want to make a permanent delete to the id and which is happening. But Somehow refresh is invoked earlier than I want. I miss something fundamental here.
function submitAndRefresh(){
var deletePromisesTasks = [];
var addPromisesTasks = [];
angular.forEach(toBeDeleted, function(value,key){
deletePromisesTasks.push(delete(value.id));
});
angular.forEach(toBeAdded, function(value,key){
addPromisesTasks.push(add(value));
});
//To be performed after all the add/delete operations are completely done!
$q.all(deletePromisesTasks,addPromisesTasks).then(function(){
refreshData(); //Another Service Call
});
}
function delete(id, purge_flag) {
var defer = $q.defer();
deleteOrPurge(id, purge_flag).then((promise1) => {
defer.resolve();
}).catch(function(error) {
confirmAndDeleteContractParameters(error, delete, id, true);
}).finally(function(){
return defer.promise;
});
}
function deleteOrPurge(id, purge_flag){
return Service.items().delete({
id: id,
purge_flag: purge_flag || false
}).$promise;
}
function confirmAndDelete(error, callback, parameterId, purge_flag) {
var confirmModal = {
header: 'Confirm',
showCancelButton: true
};
try {
confirmModal.body = 'Some msg. Do you want to continue?';
} catch (e) {}
ModalService.confirmDialog($scope, confirmModal, function(confirm){
if (confirm) {
callback(parameterId, purge_flag);
}
});
}
As a basic rule, when you have a function started with var defer = $q.defer();, the last line of the function should usually be return defer.promise
I want to validate some values with javascript and the though process is like this:
I fire an event when user submits form
Javascript gets the values and validates them. If they are correct continue with number three, if not, return abortively.
Make ajax request
Here is my submit function:
submitLoginForm: function (event) {
event.preventDefault();
// get the values
var object = this._getFormValues();
// make ajax request
this.querySelector('#postLoginForm').body = object;
this.querySelector('#postLoginForm').generateRequest();
},
This is the function that grabs the values
_getFormValues: function () {
var email = this.querySelector('#email').value;
var password = this.querySelector('#password').value;
var remember = this.querySelector('#remember').value;
this._validate(email, password);
return { 'email': email, 'password': password, 'remember': remember };
}
And this is the function that validates the fields or returns
_validate: function (email, password) {
if (email === '' || password === '') {
var elements = this.querySelectorAll('.form-group');
for (var i = 0; i < elements.length; i++) {
elements[i].className += ' has-error';
};
this.hasError = true;
this.errorMessage = 'Email and Password are required fields.'
return;
}
},
Till now I used early return like this:
if ('email' === '') {
return;
}
makeAjaxRequest();
But since I have nested functions, I wonder how to abort the process completely, if the fields don't validate.
Something like an exit() function would be nice.
A better approach would be to just decouple some of your nesting. Prefer returning a bool to make control flow like this easy. So, just moving some of your code around, I would do this:
Your submit function:
submitLoginForm: function (event) {
event.preventDefault();
// get the values
var object = this._getFormValues();
if(this._validates(object)) {
// make ajax request
this.querySelector('#postLoginForm').body = object;
this.querySelector('#postLoginForm').generateRequest();
} else {
// moved this from validation
var elements = this.querySelectorAll('.form-group');
for (var i = 0; i < elements.length; i++) {
elements[i].className += ' has-error';
};
}
},
get form values
_getFormValues: function () {
var email = this.querySelector('#email').value;
var password = this.querySelector('#password').value;
var remember = this.querySelector('#remember').value;
return {
'email': email,
'password': password,
'remember': remember
};
}
Your validator:
_validate: function (values) {
var email = values.email
var passw = values.password
if (email === '' || password === '') {
return false
} else {
return true
}
},
I think I got everything - but it's a starting point anyway. Decouple each function from other tasks and it makes everything much easier.
I think an improved design here would be a better approach. Why does your _getFormValues do any validation? If you decouple the two functions your code is both more clear and you can easily "early return". I would 1. Save what is returned from _getFromValues into a variable 2. Query that Object for empty values ('email === '')3. Then if appropriate fields are filled in validate then do ajax call. If not, notify the user and DO NOT validate or ajax.
I would have a look at some Promises implementations.
jQuery $.Deferred() object or Q implementations. It also works great if your nested functions must run asynchronously.
//quick example
submitLoginForm: function (event) {
var data = _getFormValues();
_submit(data).then(
function () {
//SUCCESS
//update UI accordantly
},
function (error) {
//FAIL
console.log(error);
}
);
}
_submit: function (data) {
var $q = new $.Deferred();
//AT ANY POINT, run $q.resolve() if you've got a success;
//OTHERWISE, run $q.reject() to trigger a failure.
if (_validate() == false) {
$q.reject("validation failed");
}
makeAjax("/api/foo", function () {
//ajax success
$q.resolve(jsonFromServer);
})
return deferredObj.promise();
}
Your function _submit() will return a promise object that will then run a success or fail function depending on if you run a resolve or reject call. This is great for sync or async with several nested functions.
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.