Search code examples
kendonumerictextboxkendo-validator

Kendo Numeric text box custom validation for percentage value


We are trying to display a validation error message by overriding the default behavior of Kendo numerictextbox for displaying percentage value.

our expectation is to provide a custom message when user type in any value more than 100.

By default Kendo NumericTextbox for percentage auto corrects the value if the user type in anything more than 100 (we don't want this behavior)

Please find a jsfiddle reference URL for the same to understand it better https://jsfiddle.net/6uyp825h/57/

<!DOCTYPE html>
<html>
<head>
<base href="https://demos.telerik.com/kendo-ui/numerictextbox/index">
<style>html { font-size: 14px; font-family: Arial, Helvetica, sans-serif; }</style>
<title></title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.2.620/styles/kendo.common-material.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.2.620/styles/kendo.material.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.2.620/styles/kendo.material.mobile.min.css" />

<script src="https://kendo.cdn.telerik.com/2018.2.620/js/jquery.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2018.2.620/js/kendo.all.min.js"></script>

</head>
<body>

    <div id="example">
        <div id="add-product" class="demo-section k-content">
            <p class="title">Add new product</p>
            <ul id="fieldlist">
                <li>
                    <label>
                        Price Discount:
                        <input id="percentage" value="5" title="percentage" style="width: 100%;" />
                    </label>
                </li>

            </ul>
        </div>


        <script>
            $(document).ready(function() {

                // create Percentage NumericTextBox from input HTML element
                $("#percentage").kendoNumericTextBox({
                    format: "##.00 \\%",
                    min: 0,
                    spinner: false
                });

    var container = root;
    kendo.init(container);

    container.kendoValidator({
        rules: {
            checkPercentageMaxValue: function (input) {

               var maxAllowedValue = 100;
               var currentValue = parseInt($("#percentage").val());
                        if (currentValue > maxAllowedValue)
                        {
                            return false;
                        }
                        else {
                            return true;
                        }
                    return true;
            }
        },
        messages: {

            checkPercentageMaxValue: "Percentage value cannot be greater than 100."
        }
    });


            });
        </script>

        <style>
            .demo-section {
                padding: 0;
            }

            #add-product .title {
                font-size: 16px;
                color: #fff;
                background-color: #1e88e5;
                padding: 20px 30px;
                margin: 0;
           }

           #fieldlist {
               margin: 0 0 -1.5em;
               padding: 30px;
           }

           #fieldlist li {
               list-style: none;
               padding-bottom: 1.5em;
           }

           #fieldlist label {
               display: block;
               padding-bottom: .6em;
               font-weight: bold;
               text-transform: uppercase;
               font-size: 12px;
           }

           #fieldlist label .k-numerictextbox {
               font-size: 14px;
           }
        </style>

    </div>

Here is the html which I gets in real scenario

<div class="col-sm-8">
        <span class="k-widget k-numerictextbox single-line text-box form-control">
            <span class="k-numeric-wrap k-state-default k-expand-padding">
                <input tabindex="0" title="112.00 %" class="k-formatted-value single-line text-box form-control k-input k-valid" role="spinbutton" aria-disabled="false" aria-valuenow="112" style="display: inline-block;" type="text">
                <input name="PercentHeld3" class="single-line text-box form-control k-input k-valid" id="PercentHeld3" role="spinbutton" aria-disabled="false" aria-valuenow="112" style="display: none; border-color:black; " type="text" maxlength="16" data-role="numerictextbox" data-bind="value: PercentHeld" data-spinners="false" data-numberformat="percentage" data-decimals="2" data-validate="true" data-maxallowedvalue="100" data-max-msg="Percentage value cannot be greater than 100.">
                <span class="k-select" style="display: none;">
                    <span title="Increase value" class="k-link k-link-increase" style="touch-action: none;" aria-label="Increase value" unselectable="on">
                        <span class="k-icon k-i-arrow-60-up" unselectable="on"></span>
                    </span>
                    <span title="Decrease value" class="k-link k-link-decrease" style="touch-action: none;" aria-label="Decrease value" unselectable="on">
                        <span class="k-icon k-i-arrow-60-down" unselectable="on"></span>
                    </span>
                </span>
            </span>
        </span>
    </div>

Rule used in real scenario

checkPercentageMaxValue: function (input) {
                $('input[data-maxallowedvalue][data-validate="true"]').each(function (index, item) {
                    var maxAllowedValue = parseInt($(item).attr('data-maxallowedvalue'));
                    var currentValue = parseInt($(item).val().replace(/%?$/, ''));
                    if (currentValue > maxAllowedValue) {
                        return false;
                    }
                    else {
                        return true;
                    }
                });
                return true;
            }

Solution

  • Right after some trial and error and finally understanding what it is you want. I think you have misunderstood how the validator rules work.

    http://dojo.telerik.com/UHIhACOh

    The rules: checkPercentageMaxValue: function(input) {

         var valid = true;
         singlerule += 1;
         if ($(input).is('[data-maxallowed-value][data-validate="true"]')) {
           var maxAllowedValue = parseFloat($(input).attr('data-maxallowed-value'));
           var currentValue = parseFloat($(input).val());
           if (isNaN(currentValue)) {
             currentValue = 0;
           }
           valid = (currentValue <= maxAllowedValue);
           $('#control1').html('Max Allowed Value::' + maxAllowedValue + ',Current Parsed Value::' + currentValue);
         } else {
           $('#control1').html('<pre><code>' + JSON.stringify($(input), null, 4) + '</code></pre>');
    
         }
    
         $('#control1').append('I ran <b>checkPercentageMaxValue</b> rule: ' + singlerule + 'time(s)<br/>');
    
    
         singlerule = 0;
    
         return valid;
       },
       yourrule: function(input) {
         $('input[data-maxallowed-value][data-validate="true"]').each(function(index, item) {
           multirule += 1;
            $('#control1').append('I ran <b>yourrule</b> rule: ' + multirule + 'time(s)<br/>');
           var maxAllowedValue = parseFloat($(item).attr('data-maxallowed-value'));
           var currentValue = parseFloat($(item).val());
           if (isNaN(currentValue)) {
             currentValue = 0;
           }
           if (currentValue > maxAllowedValue) {
             return false;
           } else {
             return true;
           }
         });
    
    
         multirule = 0;
         return true;
    
       }
    
     }
    

    Each rule listed in the custom rules list is ran on every control that is being validated.

    So your current rule is checking each input control and then any other input controls of that type again assuming they meet the approved condition.

    So you are running the checks multiple times and if one passes then your rule assumes all have passed if it hits a true condition and the same is true for an error'd item which is why you are getting the issues you are currently getting. Obviously if this is what you want (checking multiple items are the same time) then you need to hold the true/false value in a variable so that it will return back the message (but this message will only be displayed next the control that kicked off the initial validation/focus)

    Hopefully adding the second control will show you what is happening clearer.

    As you can see the first time you enter a control your rule will run twice until one of the input boxes states that is in an error state and then you rule will only run when one of the controls is in a invalid state, if you put a value incorrectly in the second box then your rule states that it has exited successfully and is valid.

    I added a button so you can see if the validator thinks it is in a valid/invalid state based on the rules provided.

    If anything isn't clear or you need more info let me know and I will update the answer accordingly.