Javascript validation doesn't stop model save

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

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?

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.

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 rather than calling return true;

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?

Not that I can think of — the only supported way to do pre-save validation logic is to have a custom Save button.

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’);
    //if(!requiredFieldsEntered) {$.unblockUI(); return false;} 
    // reload data in CostCenters data model
    EventCodes.updateData(function() {
       $.unblockUI(); return false;
console.log(‘Error duplicate Event Code’);
$.unblockUI(); return false;
           // Do save


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:…

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.

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; }