Introduction

Usually, while using validation in Kendo Grid, you will find some design issues in the Grid when displaying the warning/error message. This blog will tell you how to fix this issue..

Kendo Grid with validation

I will implement the Kendo Grid by referring http://demos.telerik.com/kendo-ui/grid/editing

KendoGrid.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>Untitled</title>
  6. <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2017.1.118/styles/kendo.common.min.css">
  7. <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2017.1.118/styles/kendo.rtl.min.css">
  8. <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2017.1.118/styles/kendo.default.min.css">
  9. <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2017.1.118/styles/kendo.mobile.all.min.css">
  10. <script src="http://code.jquery.com/jquery-1.12.3.min.js"></script>
  11. <script src="http://kendo.cdn.telerik.com/2017.1.118/js/angular.min.js"></script>
  12. <script src="http://kendo.cdn.telerik.com/2017.1.118/js/jszip.min.js"></script>
  13. <script src="http://kendo.cdn.telerik.com/2017.1.118/js/kendo.all.min.js"></script></head>
  14. <body>
  15. <div id="example">
  16. <div id="grid"></div>
  17. <script>
  18. $(document).ready(function () {
  19. var crudServiceBaseUrl = "https://demos.telerik.com/kendo-ui/service",
  20. dataSource = new kendo.data.DataSource({
  21. transport: {
  22. read: {
  23. url: crudServiceBaseUrl + "/Products",
  24. dataType: "jsonp"
  25. },
  26. update: {
  27. url: crudServiceBaseUrl + "/Products/Update",
  28. dataType: "jsonp"
  29. },
  30. destroy: {
  31. url: crudServiceBaseUrl + "/Products/Destroy",
  32. dataType: "jsonp"
  33. },
  34. create: {
  35. url: crudServiceBaseUrl + "/Products/Create",
  36. dataType: "jsonp"
  37. },
  38. parameterMap: function(options, operation) {
  39. if (operation !== "read" && options.models) {
  40. return {models: kendo.stringify(options.models)};
  41. }
  42. }
  43. },
  44. batch: true,
  45. pageSize: 20,
  46. schema: {
  47. model: {
  48. id: "ProductID",
  49. fields: {
  50. ProductID: { editable: false, nullable: true },
  51. ProductName: { validation: { required: true } },
  52. UnitPrice: { type: "number", validation: { required: true, min: 1} },
  53. Discontinued: { type: "boolean" },
  54. UnitsInStock: { type: "number", validation: { min: 0, required: true } }
  55. }
  56. }
  57. }
  58. });
  59. $("#grid").kendoGrid({
  60. dataSource: dataSource,
  61. navigatable: true,
  62. pageable: true,
  63. height: 550,
  64. toolbar: ["create", "save", "cancel"],
  65. columns: [
  66. "ProductName",
  67. { field: "UnitPrice", title: "Unit Price", format: "{0:c}", width: 120 },
  68. { field: "UnitsInStock", title: "Units In Stock", width: 120 },
  69. { field: "Discontinued", width: 120 },
  70. { command: "destroy", title: " ", width: 150 }],
  71. editable: true
  72. });
  73. });
  74. </script>
  75. </div>
  76. </body>
  77. </html>

Result in browser

From the above figure, you can notice there is a break in error message. In the last row of Grid, the user needs to scroll down manually to check the error message. This is somewhat odd from a user's perspective. Let’s see how to fix it with simple logic which is written in the below function.
  1. $(function () {
  2. var grid = $("#grid").data("kendoGrid");
  3. grid.table.on("focusout", ".k-invalid", function () {
  4. var content = grid.content;
  5. var height = content.height();
  6. var cell = $(this).closest("td");
  7. var message = cell.find(".k-invalid-msg");
  8. var callout = message.find(".k-callout");
  9. var position = message.position();
  10. var top = position.top + callout.outerHeight() + message.outerHeight();
  11. if (top > height) {
  12. content.scrollTop(content.scrollTop() + top - height);
  13. }
  14. });
  15. });
Complete code
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>Untitled</title>
  6. <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2017.1.118/styles/kendo.common.min.css">
  7. <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2017.1.118/styles/kendo.rtl.min.css">
  8. <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2017.1.118/styles/kendo.default.min.css">
  9. <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2017.1.118/styles/kendo.mobile.all.min.css">
  10. <script src="http://code.jquery.com/jquery-1.12.3.min.js"></script>
  11. <script src="http://kendo.cdn.telerik.com/2017.1.118/js/angular.min.js"></script>
  12. <script src="http://kendo.cdn.telerik.com/2017.1.118/js/jszip.min.js"></script>
  13. <script src="http://kendo.cdn.telerik.com/2017.1.118/js/kendo.all.min.js"></script></head>
  14. <body>
  15. <div id="example">
  16. <div id="grid"></div>
  17. <script>
  18. $(document).ready(function () {
  19. $(function () {
  20. var grid = $("#grid").data("kendoGrid");
  21. grid.table.on("focusout", ".k-invalid", function () {
  22. var content = grid.content;
  23. var height = content.height();
  24. var cell = $(this).closest("td");
  25. var message = cell.find(".k-invalid-msg");
  26. var callout = message.find(".k-callout");
  27. var position = message.position();
  28. var top = position.top + callout.outerHeight() + message.outerHeight();
  29. if (top > height) {
  30. content.scrollTop(content.scrollTop() + top - height);
  31. }
  32. });
  33. });
  34. var crudServiceBaseUrl = "https://demos.telerik.com/kendo-ui/service",
  35. dataSource = new kendo.data.DataSource({
  36. transport: {
  37. read: {
  38. url: crudServiceBaseUrl + "/Products",
  39. dataType: "jsonp"
  40. },
  41. update: {
  42. url: crudServiceBaseUrl + "/Products/Update",
  43. dataType: "jsonp"
  44. },
  45. destroy: {
  46. url: crudServiceBaseUrl + "/Products/Destroy",
  47. dataType: "jsonp"
  48. },
  49. create: {
  50. url: crudServiceBaseUrl + "/Products/Create",
  51. dataType: "jsonp"
  52. },
  53. parameterMap: function(options, operation) {
  54. if (operation !== "read" && options.models) {
  55. return {models: kendo.stringify(options.models)};
  56. }
  57. }
  58. },
  59. batch: true,
  60. pageSize: 20,
  61. schema: {
  62. model: {
  63. id: "ProductID",
  64. fields: {
  65. ProductID: { editable: false, nullable: true },
  66. ProductName: { validation: { required: true } },
  67. UnitPrice: { type: "number", validation: { required: true, min: 1} },
  68. Discontinued: { type: "boolean" },
  69. UnitsInStock: { type: "number", validation: { min: 0, required: true } }
  70. }
  71. }
  72. }
  73. });
  74. $("#grid").kendoGrid({
  75. dataSource: dataSource,
  76. navigatable: true,
  77. pageable: true,
  78. height: 550,
  79. toolbar: ["create", "save", "cancel"],
  80. columns: [
  81. "ProductName",
  82. { field: "UnitPrice", title: "Unit Price", format: "{0:c}", width: 120 },
  83. { field: "UnitsInStock", title: "Units In Stock", width: 120 },
  84. { field: "Discontinued", width: 120 },
  85. { command: "destroy", title: " ", width: 150 }],
  86. editable: true
  87. });
  88. });
  89. </script>
  90. </div>
  91. </body>
  92. </html>

Result in Browser

I hope you have enjoyed this blog. Your valuable feedback, questions, and comments about this blog are always welcome.