Magic real time error/warning rendering using javascript snippet on a Table row??

Kaede HollandKaede Holland ✭✭
edited August 2017 in Questions
I want my users to notice right away when they are making a mistake. 

We have a table of Project records, which includes a field showing the amount of revenue we have remaining to recognize. We have another field, "Current Month Estimate," that we use to estimate how much of this remaining revenue we'll be able to recognize this month. 

If we only have $500 remaining to recognize, and a user types in $90,000 in the estimate field, I need something to happen. Since I can't send electrical shocks through salesforce, I am hoping I can use some kind of skuid javascript magic to prevent the user from making this kind of error. 

Validation rules are not an option. 

It would be great if it were a real time error popup or rendering the background of the field red, or preventing a save to that row and showing an error message...

Any ideas?

Comments

  • Rob HatchRob Hatch 🛠️ 
    edited September 2016
    In our next release,  Skuid will be including a new "ElectroZap" component.   Just like rendering conditions,  you dictate the data conditions that activate,  and there are optional properties for how big a jolt it delivers. 

    (Oh - its not still April 1st is it? )

    Seriously.  In javascript you should be able to evaluate some data condition in real time - and if the condition is true turn on a style.  Maybe the whole page background could turn red,  or a div could be changed from hidden to exposed.  Etc. 

  • edited March 2017
    Here is an example I threw together yesterday. I use a Custom Render Snippet on the Amount field on a Table of Opportunities to prevent users from entering an Amount > $1 Billion. If they do, I revert the user's input to whatever value they last entered, and display a red field-level error message.

    Here's the end product:



    And here's how you do it:

    1. add an Inline CSS resource, to define the styles we want to apply to the field when the user's input is bad.
    .field-error-messages {    
       color: red;
        padding: 2px;
    }
    .my-required-field textarea, .my-required-field input  {
        border: 1px solid #D00;
        border-right: 4px solid #D00;
    }


    2. add a new JavaScript Resource of type Inline (Snippet), called "AmountSanityCheck", with the following Body:
    var field = arguments[0],    
       value = skuid.utils.decodeHTML(arguments[1]),
        $ = skuid.$;
        
    // Run the default renderer    
    skuid.ui.fieldRenderers[field.metadata.displaytype][field.mode](field,value);
    if (field.mode === 'edit') {
        
        var errorMessageBox;
        var addFieldError = function(errorMessage) {
            if (!errorMessageBox) {
               errorMessageBox = field.element.find('.field-error-messages');
               if(!errorMessageBox.length) {
                   errorMessageBox = $('<div class="field-error-messages">');
                   field.element.append(errorMessageBox);
               }
           }
           errorMessageBox.show();
           field.element.addClass('my-required-field');
           errorMessageBox.text(errorMessage);
        };
        
        var input = field.element.find(':input');
        
        var MAX_VALUE = 1000000000;
        var inputValueIsBad = function(inputValue) {
            return parseFloat(inputValue,10)>=MAX_VALUE;
        }
        
        skuid.utils.delayInputCallback(input,function(newValue,oldValue){
            var val = input.val();
           if (inputValueIsBad(input.val())) {
               // Add an error
               addFieldError('Yeah right, a deal for $1B+? Check your numbers dude.');
               // And revert the value
               if (inputValueIsBad(oldValue)) oldValue = 999999999;
               field.model.updateRow(field.row,field.id,oldValue);
               input.val(oldValue);
           }  else {
               if (errorMessageBox) errorMessageBox.hide();
               field.element.removeClass('my-required-field');
           }
        });
    }


    3. Set your Amount field to use the AmountSanityCheck custom render snippet.




  • edited February 2015
    Thanks for the solution Zach. I tried this solution on a custom field of Date type. When I enter the date from Datepicker this is not working. It works if I punch in the date manually. Is there a different approach when datepicker is involved?
  • edited December 2016
    Hi Nayana, here is a version that will work for Date fields:

    var field = arguments[0],       
        value = skuid.utils.decodeHTML(arguments[1]),
        $ = skuid.$;
        
    if (field.mode === 'edit') {
        
        // We'll do our own Date rendering logic
        var datePicker = skuid.ui.renderers.DATE.edit({
           value : value,
           onChange : function(newValue,oldValue) {
              checkValue(newValue,oldValue);
           }
        });
        
        // This logic adds an error message in a "field-error-messages" area
        var errorMessageBox;
        var addFieldError = function(errorMessage) {
            if (!errorMessageBox) {
               errorMessageBox = field.element.find('.field-error-messages');
               if(!errorMessageBox.length) {
                   errorMessageBox = $('<div class="field-error-messages">');
                   field.element.append(errorMessageBox);
               }
           }
           errorMessageBox.show();
           field.element.addClass('my-required-field');
           errorMessageBox.text(errorMessage);
        };
        
        var minDate = new Date();
        var inputValueIsBad = function(dateValue) {
            // If the Stage is NOT Closed,
            // then make sure the date is after today
            if ($.inArray(field.model.getFieldValue(field.row,'StageName',true),['Closed Won','Closed Lost'])===-1) {
                return skuid.time.parseSFDate(dateValue) < minDate;
            } else {
                // Otherwise allow any values at all
                return false;
            }
        }
        
        var doUpdate = function(val){
            field.model.updateRow(field.row,field.id,val,{ initiatorId : field._GUID});
        };
        
        var checkValue = function(newValue,oldValue){
           if (inputValueIsBad(newValue)) {
               // Add an error
               addFieldError('Close Date must be after today.');
               // And revert the value
               // If the OLD value is bad, then set it to Today
               if (inputValueIsBad(oldValue)) oldValue = skuid.time.getSFDate(minDate);
               // Set our field's value back to the old value
               doUpdate(oldValue);
           }  else {
               // Hide the error message
               if (errorMessageBox) errorMessageBox.hide();
               field.element.removeClass('my-required-field');
               // And perform the update with the new value
               doUpdate(newValue);
           }
        };
        
        field.element.append(datePicker);
        
    } else {
        // Run the default renderer    
        skuid.ui.fieldRenderers[field.metadata.displaytype][field.mode](field,value);
    }
  • edited February 2015
    That worked! Thank you Zach!!
  • edited February 2015
    But the variable oldValue on checkValue(newValue,oldValue) is returning a null value for me.
  • Emily DavisEmily Davis ✭✭
    edited December 2016
    Nayana, I think the onChange function actually only takes the new value. In the edit mode renderer, I tried replacing
    onChange : function(newValue,oldValue) {
     checkValue(newValue,oldValue);
    }
    with 
    var datePicker = skuid.ui.renderers.DATE.edit({        value : value,
           onChange : function(newValue) {
              checkValue(newValue,value);
           }
        });
    No guarantee that that's the best way to do it, but it captures the old value.

  • edited November 2015
    Is this delayInputCallback() any method in skuid.utils API?
  • edited December 2016
    This method has been added to the skuid.utils API docs.
Sign In or Register to comment.