Let's say I have a data like this:
[
{ID: 1, SomeForeignKeyID: 4, IsFkEnabled: true},
{ID: 2, SomeForeignKeyID: 9, IsFkEnabled: false}
]
Kendo Grid is using this data:
columns.Bound(m => m.ID);
columns.ForeignKey(p => p.SomeForeignKeyID, ViewBag.ForeignKeys as IEnumerable<object>, "Value", "Name");
Here's the problem: how to make ForeignKey column editable, but only in rows, where IsFkEnabled == true? Edit mode is InCell.
None of these approaches worked for me. A very simple implentation looks like this
edit: function (e) {
e.container.find("input[name='Name']").each(function () { $(this).attr("disabled", "disabled") });
}
Where edit is part of the kendo grid declaration and Name is the actual name of the field.
Please try with the below code snippet.
VIEW
<script type="text/javascript">
function errorHandler(e) {
if (e.errors) {
var message = "Errors:\n";
$.each(e.errors, function (key, value) {
if ('errors' in value) {
$.each(value.errors, function () {
message += this + "\n";
});
}
});
alert(message);
}
}
function onGridEdit(arg) {
if (arg.container.find("input[name=IsFkEnabled]").length > 0) {
arg.container.find("input[name=IsFkEnabled]").click(function () {
if ($(this).is(":checked") == false) {
}
else {
arg.model.IsFkEnabled = true;
$("#Grid").data("kendoGrid").closeCell(arg.container);
$("#Grid").data("kendoGrid").editCell(arg.container.next());
}
});
}
if (arg.container.find("input[name=FID]").length > 0) {
if (arg.model.IsFkEnabled == false) {
$("#Grid").data("kendoGrid").closeCell(arg.container)
}
}
}
</script>
<div>
@(Html.Kendo().Grid<MvcApplication1.Models.TestModels>()
.Name("Grid")
.Columns(columns =>
{
columns.Bound(p => p.ID);
columns.Bound(p => p.Name);
columns.Bound(p => p.IsFkEnabled);
columns.ForeignKey(p => p.FID, (System.Collections.IEnumerable)ViewData["TestList"], "Value", "Text");
})
.ToolBar(toolBar => toolBar.Save())
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Pageable()
.Sortable()
.Scrollable()
.Filterable()
.Events(e => e.Edit("onGridEdit"))
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.ServerOperation(false)
.Events(events => events.Error("errorHandler"))
.Model(model =>
{
model.Id(p => p.ID);
model.Field(p => p.ID).Editable(false);
})
.Read(read => read.Action("ForeignKeyColumn_Read", "Home"))
.Update(update => update.Action("ForeignKeyColumn_Update", "Home"))
)
)
</div>
MODEL
namespace MvcApplication1.Models
{
public class TestModels
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsFkEnabled { get; set; }
public int FID { get; set; }
}
}
CONTROLLER
public class HomeController : Controller
{
public ActionResult Index()
{
List<SelectListItem> items = new List<SelectListItem>();
for (int i = 1; i < 6; i++)
{
SelectListItem item = new SelectListItem();
item.Text = "text" + i.ToString();
item.Value = i.ToString();
items.Add(item);
}
ViewData["TestList"] = items;
return View();
}
public ActionResult ForeignKeyColumn_Read([DataSourceRequest] DataSourceRequest request)
{
List<TestModels> models = new List<TestModels>();
for (int i = 1; i < 6; i++)
{
TestModels model = new TestModels();
model.ID = i;
model.Name = "Name" + i;
if (i % 2 == 0)
{
model.IsFkEnabled = true;
}
model.FID = i;
models.Add(model);
}
return Json(models.ToDataSourceResult(request));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ForeignKeyColumn_Update([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<TestModels> tests)
{
if (tests != null && ModelState.IsValid)
{
// Save/Update logic comes here
}
return Json(ModelState.ToDataSourceResult());
}
}
If you want to download demo then click here.
The simplest way is to use the dataBound event to conditionally apply one of the special CSS classes to the cells that the grid ignore for editing:
dataBound: function(e) {
var colIndex = 1;
var rows = this.table.find("tr:not(.k-grouping-row)");
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
var model = this.dataItem(row);
if (!model.Discontinued) {
var cell = $($(row).find("td")[colIndex]);
cell.addClass("k-group-cell");
}
}
},
Notes:
Approach #1
Use the grid's edit event and then do something like this:
$("#grid").kendoGrid({
dataSource: dataSource,
height: "300px",
columns: columns,
editable: true,
edit: function (e) {
var fieldName = e.container.find("input").attr("name");
// alternative (if you don't have the name attribute in your editable):
// var columnIndex = this.cellIndex(e.container);
// var fieldName = this.thead.find("th").eq(columnIndex).data("field");
if (!isEditable(fieldName, e.model)) {
this.closeCell(); // prevent editing
}
}
});
/**
* @returns {boolean} True if the column with the given field name is editable
*/
function isEditable(fieldName, model) {
if (fieldName === "SomeForeignKeyID") {
// condition for the field "SomeForeignKeyID"
// (default to true if defining property doesn't exist)
return model.hasOwnProperty("IsFkEnabled") && model.IsFkEnabled;
}
// additional checks, e.g. to only allow editing unsaved rows:
// if (!model.isNew()) { return false; }
return true; // default to editable
}
Demo here (updated for Q1 2014)
To use this via the MVC fluent syntax, simply give the anonymous edit
function above a name (e.g. onEdit
):
function onEdit(e) {
var fieldName = e.container.find("input").attr("name");
// alternative (if you don't have the name attribute in your editable):
// var columnIndex = this.cellIndex(e.container);
// var fieldName = this.thead.find("th").eq(columnIndex).data("field");
if (!isEditable(fieldName, e.model)) {
this.closeCell(); // prevent editing
}
}
and reference it like this:
@(Html.Kendo().Grid()
.Name("Grid")
.Events(events => events.Edit("onEdit"))
)
The disadvantage to this is that the editor gets created first before the edit event is triggered, which can sometimes have undesirable visual effects.
Approach #2
Extend the grid by overriding its editCell
method with a variation that triggers a beforeEdit
event; for that to work with grid options, you'll also need to override the init method:
var oEditCell = kendo.ui.Grid.fn.editCell;
var oInit = kendo.ui.Grid.fn.init;
kendo.ui.Grid = kendo.ui.Grid.extend({
init: function () {
oInit.apply(this, arguments);
if (typeof this.options.beforeEdit === "function") {
this.bind("beforeEdit", this.options.beforeEdit.bind(this));
}
},
editCell: function (cell) {
var that = this,
cell = $(cell),
column = that.columns[that.cellIndex(cell)],
model = that._modelForContainer(cell),
event = {
container: cell,
model: model,
field: column.field
};
if (model && this.trigger("beforeEdit", event)) {
// don't edit if prevented in beforeEdit
if (event.isDefaultPrevented()) return;
}
oEditCell.call(this, cell);
}
});
kendo.ui.plugin(kendo.ui.Grid);
then use it similar to #1:
$("#grid").kendoGrid({
dataSource: dataSource,
height: "300px",
columns: columns,
editable: true,
beforeEdit: function(e) {
var columnIndex = this.cellIndex(e.container);
var fieldName = this.thead.find("th").eq(columnIndex).data("field");
if (!isEditable(fieldName, e.model)) {
e.preventDefault();
}
}
});
The difference of this approach is that the editor won't get created (and focused) first. The beforeEdit
method is using the same isEditable
method from #1.
See a demo for this approach here.
If you want to use this approach with MVC wrappers but don't want / can't extend GridEventBuilder, you can still bind your event handler in JavaScript (place below the grid MVC initializer):
$(function() {
var grid = $("#grid").data("kendoGrid");
grid.bind("beforeEdit", onEdit.bind(grid));
});
Another approach is to use your own "editor" function for column definition that provides either an input element or a plain div depending on your condition.
©2020 All rights reserved.