jquerykendo-uikendo-gridtelerik-gridkendo-validator

Kendo Grid Client Template input validation


I'm currently building a grid that goes against the general way Progress mean to use their Grid but I'm hoping this is possible (I'm currently stumped).

The requirement is simple enough: show input field(s) in the client template of the grid across all rows. When clicking on a command button - ensure the input field(s) are valid before continuing.

Any which way I try the below instance, the kendo validator will not recognise either the data source schema's model field validation, OR any attributes on the input field(s) themselves. Ultimately I want to get the pattern/data-val-regex HTML5/unobtrusive validation working.

Any help would be appreciated.

The Grid I have is coded as;

$(function() {
    var Data = [];
    jQuery("#grdData").kendoGrid({
        "dataBound": grdData_Bound,
        "editable": "inline",
        "columns": [{
            "title": "Id",
            "field": "Id",
            "template": "#=FieldValueEditor(data, \"HiddenField\", \"Id\", \"\")#",
            "hidden": true
        }, {
            "title": "Name",
            "field": "Name",
            "template": "#=FieldValueEditor(data, \"TextField\", \"Name\", \".+\")#"
        }, {
            "title": "Description",
            "field": "Description"
        }, {
            "title": "Requirements",
            "field": "Requirements"
        }, {
            "title": "Icon",
            "field": "Icon"
        }, {
            "title": "Type",
            "field": "Type"
        }, {
            "title": "ActionText",
            "field": "ActionText"
        }, {
            "title": "ActionWarning",
            "field": "ActionWarning"
        }, {
            "title": "Timeout",
            "field": "Timeout"
        }, {
            "title": "Retention",
            "field": "Retention"
        }, {
            "title": "Active",
            "field": "Active"
        }, {
            "title": "New Requirements",
            "field": "NewRequirements",
            "template": "#=FieldValueEditor(data, \"TextField\", \"NewRequirements\", \".+\")#"
        }, {
            "title": "",
            "field": "Commands",
            "command": [{
                "text": "Save Requirements",
                "name": "SaveRequirements",
                "click": grdData_Command
            }, {
                "text": "Save Name",
                "name": "SaveName",
                "click": grdData_Command
            }, ]
        }, ],
        "dataSource": {
            "type": (function() {
                if (kendo.data.transports['aspnetmvc-ajax']) {
                    return 'aspnetmvc-ajax';
                } else {
                    throw new Error('The kendo.aspnetmvc.min.js script is not included.');
                }
            }
            )(),
            "transport": {
                "read": {
                    "url": "/Action/View/1/62/Data",
                    "data": AntiForgeryToken
                }
            },
            "pageSize": 4,
            "schema": {
                "data": "Data",
                "total": "Total",
                "errors": "Errors",
                "model": {
                    "id": "Name",
                    "fields": {
                        "Id": {
                            "type": "string",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                        "Name": {
                            "type": "string",
                            "validation": {
                                "required": true,
                                "pattern": ".+"
                            }
                        },
                        "Description": {
                            "type": "string",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                        "Requirements": {
                            "type": "string",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                        "Icon": {
                            "type": "string",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                        "Type": {
                            "type": "string",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                        "ActionText": {
                            "type": "string",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                        "ActionWarning": {
                            "type": "string",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                        "Timeout": {
                            "type": "object",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                        "Retention": {
                            "type": "number",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                        "Active": {
                            "type": "boolean",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                        "NewRequirements": {
                            "type": "string",
                            "validation": {
                                "required": true,
                                "pattern": ".+"
                            }
                        },
                        "Commands": {
                            "type": "object",
                            "validation": {
                                "required": true,
                                "pattern": ""
                            }
                        },
                    }
                }
            }
        }
    });
});

With this Grid, for certain cells, it calls the FieldValueEditor method to grab the Client Template;

function FieldValueEditor(data, fieldType, columnName, validationExpression) {
    var wrapper = {
        ColumnName: columnName,
        ValidationExpression: validationExpression,
        Data: data
    };
    var raw = $(`#${fieldType}-editor`).html();
    var proxy = kendo.template(raw);
    var html = proxy(wrapper);
    return html;
}

This in turn, renders one of the following script templates depending on the field type;

<script type="text/html" id="HiddenField-editor">
    <input type="hidden" class="hidden-field" data-bind="value:#:ColumnName#" />
</script>
<script type="text/html" id="TextField-editor">                
    <input type="text" class="text-field" data-bind="value:#:ColumnName#" />
</script>
<script type="text/html" id="DateField-editor">
    <input type="date" class="date-field" data-bind="value:#:ColumnName#" />
</script>
<script type="text/html" id="CheckField-editor">            
    <input type="checkbox" class="check-field" data-bind="value:#:ColumnName#"/>
</script>

Additionally, I do have some dataBound logic, ensuring the data item(s) are bound to each row;

 function grdData_Bound(e) {
    jQuery(".text-field").kendoTextBox({});
    jQuery(".date-field").kendoDatePicker({});
    $grid = $("#grdData");
    var grid = $grid.data("kendoGrid");
    $grid.find('tr.k-master-row').each(function() {
        var $row = $(this);
        $row.kendoValidator().data('kendoValidator');
        var model = grid.dataItem($row);
        kendo.bind($row, model);
    });
    $grid.focus();
}

When activating either of the two command buttons Save Name or Save Requirements, the validator from $row returns true when calling validate

  function grdData_Command(e) {
                var $row = $(e.currentTarget).closest('.k-master-row');
                var validator = $row.data('kendoValidator');
                if (validator.validate()) {
                    //always goes in here, no matter how I setup the validation
               } else {
                   //never goes in here
              }
 }

Solution

  • This was a stupid mistake on my part. In short, I was unaware that the pattern attribute does not get triggered by the kendoValidator.validate method if the field is empty. When using an empty field it appears you must use required; which ultimately makes sense and gives you the option of having an optional but strict regular expression.

    The fix was to pass in required/pattern pattern parameters to the client template like so -

    <script type="text/html" id="TextField-editor">
        <input type="text" class="text-field" data-bind="value:#:ColumnName#" #if(Required){# required#}##if(Pattern){# pattern="#=Pattern#" #}# validationMessage="Please enter a valid value" />
    </script>
    

    ..and it simply works!