-3

If you use the same name for master and detail row buttons, you receive double click events for these buttons. I think kendo ui bounds the events with "k-grid-{Your Button Name}" class attribute. Don't use same button name in master and detail rows.

name: btnName,
template: '<a class="k-button k-button-icontext k-grid-' + btnName +'" ><span class="k-icon k-i-refresh"></span></a>',
click: function (e) {
DontVoteMeDown
  • 21,122
  • 10
  • 69
  • 105

3 Answers3

1

The Grid interprets buttons with k-grid-xxx classes as built in commands which indeed should have unique names. If this is not desired you could set CSS class which does not start with the k-grid pattern and manually attach the click handler via jQuery.

Raisolution
  • 348
  • 1
  • 6
1

I wouldn't call this problem a bug. What you're experiencing is event propagation. Your detail delete button and your master delete button share the same name, and Kendo UI uses this name as part of wiring events with element class names (i.e. 'k-grid-Delete'), as you've figured out.

When you trigger the click event on the child (detail button), it propagates up the DOM tree. Lo and behold, as the event bubbles up, it finds a master delete button with the same class name, therefore, its click event triggers as well.

To stop this kind of behavior, stop the propagation in your delete button's click event.

var deleteButton = function() {
  return {
    name: "Delete",
    click: function(e) {
      // insert this line - it stops the event from bubbling up the DOM tree
      e.stopPropagation();

      var grid = this;
      var row = $(e.currentTarget).closest("tr");
      var data = this.dataItem(row);
      dWindow.content(windowTemplate(data));
      dWindow.open().center();

      $("#yesButton").click(function() {
        grid.removeRow(row);
        dWindow.close();
      });
      $("#noButton").click(function() {
        dWindow.close();
      });

      console.log('deleteButton hit!');
    }
  };
};
Brett
  • 4,268
  • 1
  • 13
  • 28
  • Thank you for your answer, but regular definition create same class elements and works correctly for both of them. If we ignore it we can have some problems. – Alim İŞÇİ May 28 '15 at 17:01
  • But you aren't using the regular definitions (i.e. `"destroy"`). You're creating custom actions, so, the framework expects you to handle conditions like this yourself. – Brett May 28 '15 at 17:40
0
I create two button functions for delete operation. 

First function name is "deleteButton()", it always returns same name 'Delete' buttons.

Second function name is "deleteButton2()" and its can returns different name buttons because of "prmName" parameter.

Please just check pruduct name on confirmation message for two buttons in detail grid. We will see different messages.

Because same name buttons triger two click events for master and detail. You can see it in debug.

Consequently we don't use same name for detail and master grid buttons.

var sampleData = [{
  ProductID: 1,
  ProductName: "Apple iPhone 5s",
  Introduced: new Date(2013, 8, 10),
  UnitPrice: 525,
  Discontinued: false,
  UnitsInStock: 10
}, {
  ProductID: 2,
  ProductName: "HTC One M8",
  Introduced: new Date(2014, 2, 25),
  UnitPrice: 425,
  Discontinued: false,
  UnitsInStock: 3
}, {
  ProductID: 3,
  ProductName: "Nokia 5880",
  Introduced: new Date(2008, 10, 2),
  UnitPrice: 275,
  Discontinued: true,
  UnitsInStock: 0
}];

function dataSource() {

  return new kendo.data.DataSource({
    transport: {
      read: function(e) {
        e.success(sampleData);
      },
      create: function(e) {
        e.data.ProductID = nextId(sampleData);
        sampleData.push(e.data);
        e.success(e.data);
      },
      update: function(e) {
        sampleData[getIndexById(sampleData, e.data.ProductID)] = e.data;
        e.success();
      },
      destroy: function(e) {
        sampleData.splice(getIndexById(sampleData, e.data.ProductID), 1);
        e.success();
      }
    },
    error: function(e) {
      alert("Status: " + e.status + "; Error message: " + e.errorThrown);
    },
    pageSize: 10,
    batch: false,
    schema: {
      model: {
        id: "ProductID",
        fields: {
          ProductID: {
            editable: false,
            nullable: true
          },
          ProductName: {
            validation: {
              required: true
            }
          },
          Introduced: {
            type: "date"
          },
          UnitPrice: {
            type: "number",
            validation: {
              required: true,
              min: 1
            }
          },
          Discontinued: {
            type: "boolean"
          },
          UnitsInStock: {
            type: "number",
            validation: {
              min: 0,
              required: true
            }
          }
        }
      }
    }
  });

};

var sampleDetailData = [{
  ProductID: 11,
  ProductName: "Detail Product 1"
}, {
  ProductID: 12,
  ProductName: "Detail Product 2"
}, {
  ProductID: 13,
  ProductName: "Detail Product 3"
}];

function detailDataSource() {

  return new kendo.data.DataSource({
    transport: {
      read: function(e) {
        e.success(sampleDetailData);
      },
      create: function(e) {
        e.data.ProductID = nextId(sampleDetailData);
        sampleDetailData.push(e.data);
        e.success(e.data);
      },
      update: function(e) {
        sampleDetailData[getIndexById(sampleDetailData, e.data.ProductID)] = e.data;
        e.success();
      },
      destroy: function(e) {
        sampleDetailData.splice(getIndexById(sampleDetailData, e.data.ProductID), 1);
        e.success();
      }
    },
    error: function(e) {
      alert("Status: " + e.status + "; Error message: " + e.errorThrown);
    },
    pageSize: 10,
    batch: false,
    schema: {
      model: {
        id: "ProductID",
        fields: {
          ProductID: {
            editable: false,
            nullable: true
          },
          ProductName: {
            validation: {
              required: true
            }
          }
        }
      }
    }
  });

};

function detailInit(e) {
  var detailRow = e.detailRow;
  detailRow.find("#detailGrid").kendoGrid({
    dataSource: detailDataSource(),
    pageable: true,
    toolbar: ["create"],
    columns: [{
      field: "ProductName",
      title: "Mobile Phone"
    }, {
      command: ["edit", deleteButton(),deleteButton2("DetailDelete")],
      title: "&nbsp;",
      width: "200px"
    }],
    editable: {
      mode: "popup",
      confirmation: false
    }
  });
};

function nextId(prmData) {
  return prmData.length + 1;
};

function getIndexById(prmData, id) {
  var idx,
    l = prmData.length;

  for (var j; j < l; j++) {
    if (prmData[j].ProductID == id) {
      return j;
    }
  }
  return null;
}

var windowTemplate = kendo.template($("#windowTemplate").html());

var dWindow = $("#window").kendoWindow({
  title: "Are you sure you want to delete this record?",
  visible: false,
  width: "400px",
  height: "200px",
}).data("kendoWindow");

var deleteButton = function() {
  return {
    name: "Delete",
    click: function(e) {
      var grid = this;
      var row = $(e.currentTarget).closest("tr");
      var data = this.dataItem(row);
      dWindow.content(windowTemplate(data));
      dWindow.open().center();

      $("#yesButton").click(function() {
        grid.removeRow(row);
        dWindow.close();
      })
      $("#noButton").click(function() {
        dWindow.close();
      })
    }
  }
};

var deleteButton2 = function(prmName) {
  return {
    name: prmName,
    click: function(e) {
      var grid = this;
      var row = $(e.currentTarget).closest("tr");
      var data = this.dataItem(row);
      dWindow.content(windowTemplate(data));
      dWindow.open().center();

      $("#yesButton").click(function() {
        grid.removeRow(row);
        dWindow.close();
      })
      $("#noButton").click(function() {
        dWindow.close();
      })
    }
  }
};

$("#grid").kendoGrid({
  dataSource: dataSource(),
  pageable: true,
  toolbar: ["create"],
  columns: [{
    field: "ProductName",
    title: "Mobile Phone"
  }, {
    field: "Introduced",
    title: "Introduced",
    format: "{0:yyyy/MM/dd}",
    width: "200px"
  }, {
    field: "UnitPrice",
    title: "Price",
    format: "{0:c}",
    width: "120px"
  }, {
    field: "UnitsInStock",
    title: "Units In Stock",
    width: "120px"
  }, {
    field: "Discontinued",
    width: "120px"
  }, {
    command: ["edit", deleteButton(),deleteButton2("MasterDelete")],
    title: "&nbsp;",
    width: "200px"
  }],
  detailTemplate: kendo.template($("#detailGridTemplate").html()),
  detailInit: detailInit,
  editable: {
    mode: "popup",
    confirmation: false
  }
});
<link href="http://cdn.kendostatic.com/2015.1.429/styles/kendo.default.min.css" rel="stylesheet" />
<link href="http://cdn.kendostatic.com/2015.1.429/styles/kendo.common.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://cdn.kendostatic.com/2015.1.429/js/kendo.all.min.js"></script>
<div id="grid"></div>
<div id="window"></div>
<script type="text/x-kendo-template" id="windowTemplate">
  Delete <strong>#= ProductName #</strong> ?</p>
  <button class="k-button" id="yesButton">Yes</button>
  <button class="k-button" id="noButton">No</button>
</script>

<script type="text/x-kendo-template" id="detailGridTemplate">
  <div id="detailGrid"></div>
</script>