Search code examples
checkboxmvvmtelerikgrid

Telerik UI MVVM grid misbehaving with checkboxes


I have a Telerik UI grid I'm using, and I can't get it to quite behave correctly with a Checkbox column.

What I'd like is to have it tie to a field in a Dataset that is boolean. I'd like to edit this item with a Checkbox. And whenever the checkbox is selected or unselected, I'd like to do some math on the row.

I've searched around Stackexchange and found some code bits that help, but it's not 100%. I've created a JSFiddle to demonstrate the odd behavior.

The grid is defined as follows:

<script type="text/x-kendo-template" id="checkboxTemplate">
    <input type="checkbox" #=Discontinued ? checked="checked" : "" # class="chkbx" />
</script>

<div
    id="LineItemsGrid"
    data-role="grid"
    data-editable="true"
    data-bind="source: LineItems"
    style="height: 470px"
    data-navigatable="true"
    data-columns="[
    { 'field': 'Discontinued', 'title':'<center>Discontinued</center>', 'width': 95, 'template': kendo.template($('#checkboxTemplate').html()) },
    { 'field': 'ProductName', 'title':'<center>Product Name</center>', 'width': 90 },
    { 'field': 'UnitPrice', 'title':'<center>Unit Price</center>', 'width': 90 },
        { 'field': 'UnitsInStock', 'title':'<center>Units In Stock</center>', 'width': 90 },
        { 'field': 'TotalInventory', 'title':'<center>Total Inventory</center>', 'width': 90 }
    ]">
</div>

Nothing too fancy. And the javascript to handle the checkboxes is here:

$(document).on('change', 'input:checkbox', function (e) {

    var grid = $("#LineItemsGrid").data("kendoGrid");
    var row = $(this).closest("tr");
    var rowIdx = $("tr", grid.tbody).index(row);
    var colIdx = $("td", row).index(this);

    var checkval = $(this).prop("checked");

    var items = LineItemsSource.data();

    // set data - this causes the move to (0,0)
    items[rowIdx].set("Discontinued", checkval);
    if(checkval)
    {
        var total =  items[rowIdx].get("UnitPrice") * items[rowIdx].get("UnitsInStock");
        items[rowIdx].set("TotalInventory",total);
    }
    else
    {
        items[rowIdx].set("TotalInventory",0);
    }
})

If you click on the checkbox, when you .set() the value to the checkbox, it moves the caret to (0,0). And when you click in the cell beside the checkbox, it moves the checkbox a bit to the left and doesn't select it. If you click it at that point it edits and doesn't move to (0,0).

I'm absolutely certain there is a more elegant way of doing this, but I'm having no luck finding any clues about it.


Solution

  • Your code was a bit hard to debug in jsfiddle.

    but I made this for you and hopefully it can help you:

    You can use the change event of the data source and MVVM instead of trying to deal with JQuery Events.

    Also by default kendo grid are not MVVM in display mode just in edit mode. If you look at the data bound event this makes your rows even in view mode mvvm.

    here is the Dojo displaying it.

    https://dojo.telerik.com/AYEsUSiz/4

    here is the code

    <div id="grid"></div>
      <script type="text/x-kendo-template" id="checkboxTemplate">
          <input type="checkbox" data-bind="checked: Discontinued" class="chkbx" />
      </script>
      <script>
            $('#grid').kendoGrid({
            dataBound: function(e) {
                let visibleModels = e.sender.dataSource.view();
                var items = e.sender.items();
                for(var i = 0 ; i < items.length; i++) {
                    kendo.bind(items[i], visibleModels[i]);
                }
            },
            editable: true,
            navigatable: true,
            dataSource: {
              change: function(e) {
                    console.log(e);
                    if (e.action == "itemchange" && e.field == 'Discontinued') {
                    var model = e.items[0];
    
                    if(model.get('Discontinued'))
                    {
                      var total =  model.get("UnitPrice") * model.get("UnitsInStock");
                      model.set("TotalInventory",total);
                    }
                    else
                    {
                      model.set("TotalInventory",0);
                    }
                  }
              },
                transport: {
                read: function(options) {
                    options.success(products); 
                }
              }
            },
            columns: [
              { 'field': 'Discontinued', 'title':'<center>Discontinued</center>', 'width': 95, 'template': kendo.template($('#checkboxTemplate').html()) },
              { 'field': 'ProductName', 'title':'<center>Product Name</center>', 'width': 90 },
              { 'field': 'UnitPrice', 'title':'<center>Unit Price</center>', 'width': 90 },
              { 'field': 'UnitsInStock', 'title':'<center>Units In Stock</center>', 'width': 90 },
              { 'field': 'TotalInventory', 'title':'<center>Total Inventory</center>', 'width': 90 }
            ]
          });
      </script>   
    

    Revision if you don't set the field the grid won't try to convert your column to an editable control and it will react the same way and changing the model inside your data source at the same time.

    https://dojo.telerik.com/AYEsUSiz/6

    code:

    <div id="grid"></div>
      <script type="text/x-kendo-template" id="checkboxTemplate">
          <input type="checkbox" data-bind="checked: Discontinued" class="chkbx" />
      </script>
      <script>
            $('#grid').kendoGrid({
            dataBound: function(e) {
                let visibleModels = e.sender.dataSource.view();
                var items = e.sender.items();
                for(var i = 0 ; i < items.length; i++) {
                    kendo.bind(items[i], visibleModels[i]);
                }
            },
            editable: true,
            navigatable: true,
            dataSource: {
              change: function(e) {
                    console.log(e);
                    if (e.action == "itemchange" && e.field == 'Discontinued') {
                    var model = e.items[0];
    
                    if(model.get('Discontinued'))
                    {
                      var total =  model.get("UnitPrice") * model.get("UnitsInStock");
                      model.set("TotalInventory",total);
                    }
                    else
                    {
                      model.set("TotalInventory",0);
                    }
                  }
              },
                transport: {
                read: function(options) {
                    options.success(products); 
                }
              }
            },
            columns: [
              { 'title':'<center>Discontinued</center>', 'width': 95, 'template': kendo.template($('#checkboxTemplate').html()) },
              { 'field': 'ProductName', 'title':'<center>Product Name</center>', 'width': 90 },
              { 'field': 'UnitPrice', 'title':'<center>Unit Price</center>', 'width': 90 },
              { 'field': 'UnitsInStock', 'title':'<center>Units In Stock</center>', 'width': 90 },
              { 'field': 'TotalInventory', 'title':'<center>Total Inventory</center>', 'width': 90 }
            ]
          });
      </script>