Javascript validation doesn't stop model save

  • 1
  • Question
  • Updated 3 years ago
  • Answered
I am using javascript as a form of validation to prevent certain combinations of fields to be saved. I know I should be using regular SalesForce validation to handle this, but according to the admins around here it would be too complex (Roll-ups on Counts on Formulas...). It works great on the creation of the object because I catch it before the save. I figured I could re-use the same snippet and have it run as an action every time the model is saved. What seems to be happening is that my logic runs and raises the error message, but the save still goes through. This is probably by design (I'm not saying that this behavior is wrong), I just would like to know if there is a way I can stop the save from happening. I tried returning false but that doesn't seem to help. Here is my code just for kicks:

var params = arguments[0],	$ = skuid.$;
var commodityName = skuid.$M('Opportunity').getFirstRow().Commodity__r.Name;
var countOfRateReadyLDC = 0;
$.each(skuid.$M('IntakeQueues').data, function(i,rowIQ){
    countOfRateReadyLDC += rowIQ.LDC__r.LDC_LDC__c;
});
var hasLBMP = false;
var hasSingleBill = false;
$.each(skuid.$M('Tenors').data, function(j,rowTenor){
    if(rowTenor.Pricing_Product__r.Name === 'LBMP+') hasLBMP = true;
    if(rowTenor.Bill_Type__c === 'Single Bill') hasSingleBill = true;
});
if(hasLBMP && hasSingleBill && (countOfRateReadyLDC > 0) && (commodityName === 'Electricity')){
    var pageTitle = $('#MyPageTitle');
    var editor = pageTitle.data('object').editor;
    editor.handleMessages([{message:'Sorry! Electric, Indexed, Single Bill deals are not allowed when an LDC is Rate Ready.', severity:'ERROR'}]);
    return false;
}else{
    return true;
}
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb

Posted 4 years ago

  • 1
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
I'm assuming that you're running this Snippet as one Action in a sequence of Actions, and that the next step is a Save? If so, returning false should stop the Action Framework from moving to the next step. Or is this not what's happening?
Photo of Rajendra Rathore

Rajendra Rathore

  • 1,480 Points 1k badge 2x thumb
Hi Zach,

I am  running Snippet as one Action in a sequence of Actions.

In First Action i perform validation and saving using snippet.
In Second action i am doing close popup and re query model.

As you say that returning false should stop the Action Framework from moving to the next step.

That works fine when i don't re query model in snippet.

But in snippet i perform re query of model and use update event of re query model and  validate that model have any duplicate records or not  (According to event code ) . 
According to model length i am returning false if any record found in model.
But that return false is not work and second action close popup is also perform in case of return false also.


According to me update event of requery model is complete in future and before it execution get passed snippet code ended point.


What you thing about this and what is possible solutions to avoid that issue? 


Here is snippet :

var params = arguments[0],        model = params.model,
        editor = params.component.element.editor,
        row = params.row,
        $ = skuid.$;
    
    var requiredFieldsEntered = validateRequiredFields(model, editor);

var EventCodes = skuid.model.getModel('EventCodes');

    EventCodesCondition = EventCodes.getConditionByName('EventCode__c');
    EventCodes.activateCondition(EventCodesCondition);
    EventCodes.setCondition(EventCodesCondition,row.EventCode__c);
    EventCodes.doQuery=true;
    //if(!requiredFieldsEntered) {$.unblockUI(); return false;} 
    // reload data in CostCenters data model
    EventCodes.updateData(function() {
       $.unblockUI(); return false;
       if(skuid.model.getModel("EventCodes").getRows().length>0){
console.log('Error duplicate Event Code');
$.unblockUI(); return false;
       }else{
           // Do save
            
       }
      
    }); 
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Rajendra,

When you're dealing with Asynchronous functions such as updateData() or save(), you have to employ a different approach to controlling whether or not to move on to the next Action in your sequence of actions, an approach based on jQuery Deferred Promises. This approach is described in this Community Post: https://community.skuidify.com/skuid/topics/block-ui-and-show-message-until-sforce-connection-remote...
Photo of Rajendra Rathore

Rajendra Rathore

  • 1,480 Points 1k badge 2x thumb
Yes Zach That post is very use full in case of  dealing with Asynchronous functions.

Thank you So much for your quick reply and all helps.
Photo of Pawan Lohani

Pawan Lohani

  • 284 Points 250 badge 2x thumb
Hi Zach,

I have similar situation in mobile page builder. I have a skuid button to run multiple action. First action is to run javascript snippet validation rule for character limit check, required field check and check for valid email. When limit exceeds or required field empty or  invalid email, javascript returns false and on-error action for  the first action is block UI and show error message, but it still fires next action.
Is there any way to stop firing next action if a rule returns false in mobile page builder.


Multiple actions on button:



Javascript snippet 'validStep1Fields':

//Define boolean variables
var limitExceeded = false;
var empty = false;
var validEmail = true;
	
//Validate Email format with Regex
var validateEmail = function(email) {
	var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
	return re.test(email); 
};

var mergedRegisteredFields = $.extend({}, clientModel.registeredFields, pmComtactModel.registeredFields, ndContactModel.registeredFields, childContactModel.registeredFields);

$.each(mergedRegisteredFields, function(i,f) {
	var dt = f.metadata.displaytype;
	var el ;
	if(dt == 'TEXTAEA') {
		el = 'textarea' ;   
	}
	else if(dt == 'CURRENCY' || dt == 'TEXT' || dt == 'STRING' || dt == 'EMAIL'){
		el = 'input';
	}
	
	var temp = f.element.find(el);
	//console.log(temp);
	var fieldValue = temp.val();
	console.log(fieldValue);
	//temp.removeClass('sff-required-field-border');
	
	var limit;
	if(f.metadata.length)
		limit = (dt == 'TEXTAREA'? 501 : f.metadata.length);
	if(fieldValue &&  fieldValue.length > limit) {
		limitExceeded = true;
		temp.addClass('sff-required-field-border');
	}
	
	if (f.required) {
		//console.log(f);
		empty =  /^\s*$/.test(temp.val());
	  if(!fieldValue || empty) {
		empty = true;
		temp.addClass('sff-required-field-border');
		}
	}
  
	if(fieldValue && dt == 'EMAIL' && !validateEmail(fieldValue)) {
	   validEmail = false;
		temp.addClass('sff-required-field-border');
	}      
}); 

  
if(limitExceeded || empty || !validEmail){
	return false;
}

else{
   return true;
}
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
No, I'm having this snippet run as an Action on the model, that runs every time the model is saved. It is not part of a sequence, it is the only action, which is set up on the model itself, to run every time the model is saved.
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Okay, that explains it --- the Model Actions are always run after the event has already taken place, not before. The best way to achieve this right now would be to have all of your Save buttons run a modified version of this Snippet that actually calls model.save() rather than calling return true;
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Thanks Zach is there a way to have my custom save override the standard save without manually creating new Save/Cancel buttons? Maybe via a handleSave type thing?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Not that I can think of --- the only supported way to do pre-save validation logic is to have a custom Save button.