I've created a grid with a dynamic object and I'd like to use the GridEditMode.InLine to update and add data. The popUp mode is working but with the InCell and
the inline I'm getting the following error:
Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
Am I missing something?
I tried to use a custom template but I am still getting the same error.
Thanks for your help
#(Html.Kendo().Grid<dynamic>()
.Name("Grid")
.DataSource(dataSource => dataSource
.Ajax()
.ServerOperation(true)
.Model(cfg =>
{
cfg.Id("SsdID");
foreach (var property in Model.PropertyDescriptors)
{
cfg.Field(property.Name, property.DataType);
}
})
.Read(cfg => cfg.Type(HttpVerbs.Post)
.Action("ReadDataForDefinition", "ManualDataEntry",
new { id = Model.LDefinitionId }))
.Update(u => u.Type(HttpVerbs.Post).Action("UpdateDataForDefinition","ManualDataEntry",
new { id = Model.LDefinitionId }))
.Create(u => u.Type(HttpVerbs.Post).Action("Create", "ManualDataEntry",
new { id = Model.LDefinitionId }))
)
.Resizable(resizing => resizing.Columns(true))
Columns(columns =>
{
foreach (var property in Model.PropertyDescriptors.Where(desc => desc.DisplayOrder.HasValue))
{
var binding = columns.Bound(property.DataType, property.Name);
if (property.DataType == typeof(DateTime) || property.DataType ==typeof(DateTime?))
binding.Format("{0:d}");
binding.Column.Title = property.Label;
}
columns.Command(command =>
{
command.Edit();
command.Destroy();
});
})
.ToolBar(toolbar => { toolbar.Create(); })
.Pageable(paging =>
{
paging.ButtonCount(10);
paging.PreviousNext(true);
paging.PageSizes(true);
})
.Editable(edit => edit.Mode(GridEditMode.InLine))
.Sortable()
.Scrollable()
.Filterable()
)
For such scenarios only PopUp edit mode is supported. InLine and InCell editing modes are not supported when the using dynamic data or DataTable because the Grid cannot resolve the types of the columns and therefore cannot create proper editor templates. You could check the type and assign templates manually as a workaround. For example:
.Columns(columns =>
{
foreach (System.Data.DataColumn column in Model.Columns)
{
switch (column.DataType.ToString())
{
case "System.Int16":
case "System.Int32":
case "System.Int64":
columns.Bound(column.ColumnName).EditorTemplateName("Integer");
break;
case "System.Decimal":
case "System.Double":
case "System.Float":
columns.Bound(column.ColumnName).EditorTemplateName("Number");
break;
case "System.String":
columns.Bound(column.ColumnName).EditorTemplateName("String");
break;
default:
//etc etc
break;
}
}
})
Related
I am using MVC application with Kendo Grid. Earlier I had checkbox column in kendo grid. However now I want to replace checkbox with 3 radio buttons having values as 1. Required 2. Optional 3. Disabled. I have managed to create radio button control using name='#: uid #'. However when I try to select multiple selection using each row I cant select proper radio button values. For example as per my grid in image if I select Disable option for Busy,CallBackLater Testing and Optional for Interest,SendInformation Disposition text then I want to send selected radio buttons enum values to controller.
Refer Kendo Grid
Enum is as below
public enum FollowUpDisposition : byte
{
Required = 1,
Optional = 2,
Disabled = 3
}
also I am using Kendo grid client template in cshtml page and code(included only required part) is as below.
#(Html.Kendo().Grid<CompanyDisposition>()
.Name("dispositionsGrid")
.Scrollable()
.HtmlAttributes(new { #style = "height: 260px;" })
.Columns(columns =>
{
/* Below is checkbox which is working*/
columns.Bound(p => p.countAsConversion).Title("Count as Conversion").Width(80).ClientTemplate("<input type='checkbox' #= countAsConversion ? 'checked=checked' : '' # data-field='countAsConversion' onchange='dispositionGridDataChange(this, this.checked)' />");
/* checkbox over */
/* Need help for this radio buttons section */
columns.Bound(p => p.IsFollowUpMandatory).Title("FollowUp").Width(80).ClientTemplate(
"<div><input type = 'radio' name='#: uid #' data-field='1' # if (IsFollowUpMandatory == '1') { # checked = 'checked' # } # onclick='dispositionGridDataChange(this, this.checked);' /> Required</div><div><input type = 'radio' name='#: uid #' data-field='2' # if (IsFollowUpMandatory == '2') { # checked = 'checked' # } # onclick='dispositionGridDataChange(this, this.checked);' /> Optional</div><div><input type = 'radio' name='#: uid #' data-field='3' # if (IsFollowUpMandatory == '3') { # checked = 'checked' # } # onclick='dispositionGridDataChange(this, this.checked);' /> Disabled</div>");
/* Radio buttons section Over*/
.ToolBar(toolbar => toolbar.Save())
.ToolBar(toolbar => toolbar.Create().HtmlAttributes(new { #style = "background-color: #A9F5A9;" }))
.Editable(editable => editable.Mode(GridEditMode.InCell).DisplayDeleteConfirmation(false))
.DataSource(dataSource => dataSource
.Ajax()
.ServerOperation(false)
.Sort(sort => sort.Add("SortOrder"))
.Events(events => events.RequestEnd("dispositionsGridRequestEnd").Change("dispositionsGridSourceChange"))
.Model(m =>
{
m.Id(p => p.CompanyDispositionId);
m.Field(f => f.DispositionType).DefaultValue("Positive");
m.Field(f => f.LeadStatus).DefaultValue("");
m.Field(f => f.ContactStatus).DefaultValue("");
m.Field(f => f.CSVStatus).DefaultValue("");
m.Field(f => f.LeadSubStatus).DefaultValue("");
m.Field(f => f.ContactSubStatus).DefaultValue("");
m.Field(f => f.SortOrder);
m.Field(f => f.SecondaryQuickListId).DefaultValue("");
m.Field(f => f.SecondaryQuickListName).DefaultValue("");
m.Field(f => f.SecondaryDispositionID).DefaultValue("");
m.Field(f => f.SecondaryDispositionName).DefaultValue("");
})
.Create(create => create.Action("CreateDisposition", "Settings"))
.Read(read => read.Action("ReadDispositions", "Settings"))
.Update(update => update.Action("UpdateDisposition", "Settings"))
.Destroy(destroy => destroy.Action("DeleteDisposition", "Settings"))))
}
My client side functions are
<script type="text/javascript">
function dispositionGridDataChange(currentElem, newValue) {
/* debugger*/
var updateField = $(currentElem).attr('data-field');
var dispositionGrid = $('#dispositionsGrid').data().kendoGrid;
var dispositionItem = dispositionGrid.dataItem($(currentElem).closest('tr'));
dispositionItem[updateField] = newValue;
dispositionItem.dirty = true;
}
</script>
Please help.
I have resolved this issue.
I was making a mistake in sending desired value to JavaScript function.
below is the updated JavaScript function which has resolved my issue.
also I removed "this.Checked" from input field because I don't need second value here. It was onchange='dispositionGridDataChange(this, this.checked)' and now I have updated it to onchange='dispositionGridDataChange(this)'
function dispositionGridDataChange(currentElem) {
var updateField = $(currentElem).attr('data-field');
var dispositionGrid = $('#dispositionsGrid').data().kendoGrid;
var dispositionItem = dispositionGrid.dataItem($(currentElem).closest('tr'));
dispositionItem.set('IsFollowUpMandatory', updateField);
dispositionItem.dirty = true;
};
I'm using Kendo UI MVC and I have a view that contains details about an object. On that page I have a Kendo UI Grid that shows a list of notes about the object. I allow the user to create or edit the notes from the grid.
The problem I have is when the user clicks the add button I need to pass the id of the page's object. I'm using GridEditMode.PopUp.
Basically, this is what I have:
public class Item {
public int Id { get;set; }
...
}
public class Note {
public int ItemId {get;set;}
...
}
Here is the grid code:
#(Html.Kendo()
.Grid<NoteViewModel>()
.Name("kendo-grid")
.Columns(columns =>
{
columns.Bound(n => n.NoteDateTime).Title("Date").Format("{0:MM/dd/yyyy}");
columns.Bound(n => n.NoteDateTime).Title("Time").Format("{0:h:mm tt}").Sortable(false);
columns.Bound(n => n.NoteActivityType).Title("Activity Type");
columns.Bound(n => n.NoteDescription).Title("Description");
columns.Bound(n => n.NoteDetail).Title("Notes");
columns.Command(command => { command.Edit(); command.Destroy(); }).Width(200);
})
.ToolBar(toolbar => toolbar.Create())
.Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("Note"))
.Mobile()
.Pageable()
.Sortable()
.Filterable()
.Reorderable(r => r.Columns(true))
.Resizable(r => r.Columns(true))
.DataSource(dataSource => dataSource.Ajax()
.Model(model => model.Id(note => note.Id))
.PageSize(25)
.Sort(sort =>
{
sort.Add(note => note.NoteDateTime);
})
.Read(read => read.Action("ReadNotes", "Case").Data("getCaseId"))
.Create(a => a.Action("CreateNote", "Case"))
.Update(u => u.Action("UpdateNote", "Case"))
.Destroy(d => d.Action("DeleteNote", "Case"))
)
)
I need to set Note.ItemId when the user clicks the add button on the grid. Or, is there a better way to do this, as in send the ItemId value on the post?
I ended up getting this to work by hooking into the edit event of the popup. I wasn't able to figure out how to do it on the initial setup, so I added this to doc ready handler to the edit popup. This kind of feels like a hack, so if someone has a better way I'd love to hear about it. The #ItemId input is already on the details page, so I figured I may as well use it.
$(function () {
function setItemId(event) {
var uid = $('.k-edit-form-container').closest('[data-role=window]').data('uid');
var model = $('#kendo-grid').data('kendoGrid').dataSource.getByUid(uid);
if (model.get('ItemId') === 0) {
model.set('ItemId', Number($('#ItemId').val()));
}
}
var grid = $('#kendo-grid').data('kendoGrid');
grid.bind('edit', setItemId);
});
I'm not sure if it's possible what you want, but to get you on the way this is how you'd start doing it.
You generally want a flat viewmodel, containing everything you want to use.
public class NoteViewModel {
public int ItemId { get;set; }
}
Then properly setup the use of your editor template. Since your ItemId is now part of the grid's model it'll be send to the controller on edit/create.
#(Html.Kendo()
.Grid<NoteViewModel>()
.Name("kendo-grid")
.Columns(columns =>
{
columns.Bound(n => n.ItemId).Hidden();
columns.Bound(n => n.NoteDateTime).Title("Date").Format("{0:MM/dd/yyyy}");
columns.Bound(n => n.NoteDateTime).Title("Time").Format("{0:h:mm tt}").Sortable(false);
columns.Bound(n => n.NoteActivityType).Title("Activity Type");
columns.Bound(n => n.NoteDescription).Title("Description");
columns.Bound(n => n.NoteDetail).Title("Notes");
columns.Command(command => { command.Edit(); command.Destroy(); }).Width(200);
})
.ToolBar(toolbar => toolbar.Create())
.Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("NoteTemplate"))
Etc...
)
Finally create your template (called NoteTemplate.cshtml) and place it in Views/Shared/EditorTemplates so Kendo can find it.
#model NoteViewModel
Date: #Html.EditorFor(l => l.NoteDateTime)
Note Description: #Html.EditorFor(l => l.NoteDescription)
(Add all fields you need to edit here)
I had same problem
Problem is that some of fields of the model(viewmodel) is nullable
Model nullable field is not fully supported in Kendo UI
I have a Kendo Grid which I would like to only be able to expand one row at a time for detail editing. What is the simplest way to do this?
#(Html.Kendo().Grid<MyModel>()
.Name("MyGrid")
.ClientDetailTemplateId("MyTemplate")
.Columns(columns =>
{
columns.Bound(b => b.Code);
columns.Bound(b => b.Name);
columns.Bound(b => b.Description);
...
columns.Command(cmd => { cmd.Edit(); cmd.Destroy(); });
})
.ToolBar(toolbar => toolbar.Create())
.Editable(editable => editable.Mode(GridEditMode.InLine))
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => model.Id(a => a.Id))
.Create(create => create.Action("Create", "SysMaint", new { id = Model.ProjectId }))
.Read(read => read.Action("Read", "SysMaint", new { projectId = Model.ProjectId }))
.Update(update => update.Action("Update", "SysMaint"))
.Destroy(destroy => destroy.Action("Destroy", "SysMaint"))
)
)
<script id="MyTemplate" type="text/kendo-tmpl">
#(Html.Kendo().TabStrip()
.Name("TabStrip_#=Id#")
.SelectedIndex(0)
.Items(items =>
{
items.Add().Text("A").LoadContentFrom("MyPartialA", "SysMaint", new { id = "#=Id#" });
items.Add().Text("B").LoadContentFrom("MyPartialB", "SysMaint", new { id = "#=Id#" });
})
.ToClientTemplate()
)
</script>
Ends up this is really simple. Just add these few lines.
...
.Update(update => update.Action("Update", "SysMaint"))
.Destroy(destroy => destroy.Action("Destroy", "SysMaint"))
)
.Events(events => events.DetailExpand("detailExpand"))
)
<script type="text/javascript">
var expandedRow;
function detailExpand(e) {
// Only one open at a time
if (expandedRow != null && expandedRow[0] != e.masterRow[0]) {
var grid = $('#MyGrid').data('kendoGrid');
grid.collapseRow(expandedRow);
}
expandedRow = e.masterRow;
}
</script>
I hope this helps somebody.
That works except it does not remove the old detail row. Add the bit marked NEW to remove each previously opened detail row.
if (expandedRow != null && expandedRow != e.masterRow[0]) {
var grid = $('#RequestsGrid').data('kendoGrid');
grid.collapseRow(expandedRow);
expandedRow[0].nextElementSibling.remove(); //NEW
}
expandedRow = e.masterRow;
Building on Trey's answer, this version will work generically for any grid (using #vikasde's suggestion), and will also work when you have nested grids, so that the child grid when invoking the detailExpand, won't collapse its parent grid row as a side effect.
<script type="text/javascript">
function detailExpand(ev) {
var expandedRow = $(ev.sender.wrapper).data('expandedRow');
// Only one open at a time
if (expandedRow && expandedRow[0] != ev.masterRow[0]) {
var grid = $(ev.sender.wrapper).data('kendoGrid');
grid.collapseRow(expandedRow);
}
$(ev.sender.wrapper).data('expandedRow', ev.masterRow);
}
</script>
Further to the answers already here, I found that by combining hatchet's and Danny Blue's answers and using the DetailCollapse event works nicely, and will remove the underlying detail content if a row is collapsed manually.
MVC Grid configuration:
.Events(ev =>
{
ev.DetailExpand("detailExpand");
ev.DetailCollapse("detailCollapse");
})
Page scripts:
function detailExpand(ev) { // Credit hatchet
var expandedRow = $(ev.sender.wrapper).data('expandedRow');
// Only one open at a time
if (expandedRow && expandedRow[0] !== ev.masterRow[0]) {
var $grid = $(ev.sender.wrapper).data('kendoGrid');
$grid.collapseRow(expandedRow);
}
$(ev.sender.wrapper).data('expandedRow', ev.masterRow);
}
function detailCollapse(ev) {
var expandedRow = $(ev.sender.wrapper).data('expandedRow');
expandedRow.next().remove(); // Credit Danny Blue
}
I am working on asp.net mvc. I am trying to display list of messages in a Kendo mvc ui grid.
I have written the code like,
Html.Kendo().Grid((List<messages>)ViewBag.Messages))
.Name("grdIndox")
.Sortable(m => m.Enabled(true).SortMode(GridSortMode.MultipleColumn))
.HtmlAttributes(new { style = "" })
.Columns(
col =>
{
col.Bound(o => o.RecNo).HtmlAttributes(new { style = "display:none" }).Title("").HeaderHtmlAttributes(new { style = "display:none" });
col.Bound(o => o.NoteDate).Title("Date").Format("{0:MMM d, yyyy}");
col.Bound(o => o.PatName).Title("Patient");
col.Bound(o => o.NoteType).Title("Type");
col.Bound(o => o.Subject);
}
)
.Pageable()
.Selectable(sel => sel.Mode(GridSelectionMode.Single).Type(GridSelectionType.Row))
.DataSource(
ds => ds.Ajax().ServerOperation(false).Model(m => m.Id(modelid => modelid.RecNo))
.PageSize(10)
//.Read(read => read.Action("Messages_Read", "Msg"))
)
.Events(ev => ev.Change("onSelectingGirdRow"))
)
and i have the field in the table like IsRead-boolean type. so if the message is unread message then i need to format that record with bold font. I have used clientTemplates but with that i am able to format only particular cells i want format entire row. Please guide me.
as Sanja suggested you can use the dataBound event but it will be better to cycle through the tr elements (the rows). Also I assume that you will need the related dataItem to check if the property that indicates if the message is read.
e.g.
dataBound: function ()
{
var grid = this;
grid.tbody.find('>tr').each(function(){
var dataItem = grid.dataItem(this);
if(!dataItem.IsMessageRead)
{
$(this).addClass('someBoldClass');
}
})
}
You can use dataBound event to change your rows.
dataBound: function ()
{
$('td').each(function(){
if(some condition...)
{
$(this).addClass('someBoldClass')}
}
})
}
There is another way to do that.It's called RowAction.
.RowAction(row =>
{
if (condition)
{
row.HtmlAttributes["class"] = "someBoldClass";
}
})
Please note that the RowAction is available only for rows that are rendered on server side. So in your case, you can use RowAction as an alternative to DataBound.
I have 2 telerik mvc ajax grids which needs to be populated based on the selected row on the first grid.
below is my code:
#(Html.Telerik().Grid<PurchaseRequest>()
.Name("grdPurchaseRequest")
.DataBinding(binding => binding.Ajax()
.Select("GetPurchaseRequests", "PurchaseOrder"))
.DataKeys(keys => keys
.Add(o => o.PurchaseRequestID))
.Columns(cols =>
{
cols.Bound(c => c.PurchaseRequestID).ReadOnly().Hidden();
cols.ForeignKey(c => c.ProjectID,(System.Collections.IEnumerable)ViewData["prProjects"],
"ProjectID", "ProjectName");
cols.Bound(c => c.FullName);
cols.Bound(c => c.RequestDate).Format("{0:MM/dd/yyyy}");
cols.Bound(c => c.Remarks);
cols.Bound(c => c.CheckedBy);
cols.Bound(c => c.ApprovedBy);
})
.ClientEvents(clientEvents => clientEvents.OnRowSelect("onRowSelected"))
.RowAction(row =>
{
row.Selected = row.DataItem.PurchaseRequestID.Equals(ViewData["id"]);
})
.Pageable()
.Sortable()
.Filterable()
.Selectable()
)
====Second GRID=====
This the the second Grid that will be populated based on the selected record on the first grid
#(Html.Telerik().Grid<PurchaseRequestDetail>().HtmlAttributes(new { style = "width: 50%" })
.Name("grdDetails")
.DataKeys(keys => keys
.Add(o => o.PurchaseRequestDetailID)
.RouteKey("PurchaseRequestDetailID"))
.Columns(cols =>
{
cols.ForeignKey(c => c.ItemID, (System.Collections.IEnumerable)ViewData["prItems"],
"ItemID", "ItemName").Width(200).Title("Description");
cols.Bound(d => d.ItemQuantity).Width(100).Title("Quantity");
cols.Bound(d => d.ItemValue).Width(100).Title("Value per quantity").Format("Php {0:###,###.00}");
cols.Bound(d => d.TotalPrice).Width(500).Format("Php {0:###,###.00}")
.Aggregate(aggs => aggs.Sum())
.ClientFooterTemplate("Php <#= $.telerik.formatString('{0:n}', Sum) #>");
})
.DataBinding(binding => binding.Ajax()
.Select("GetPurchaseRequestDetails", "PurchaseOrder", new { purchaseRequestID = "<#= PurchaseRequestID #>" }))
.ClientEvents(clientEvents => clientEvents.OnDataBinding("onDataBinding"))
.Pageable()
.Sortable()
)
the Script code
<script type="text/javascript">
var purchaseRequestID;
var purchaseRequestName;
function onRowSelected(e) {
var detailsGrid = $('#grdDetails').data('tGrid');
purchaseRequestID = e.row.cells[0].innerHTML;
purchaseRequestName = e.row.cells[1].innerHTML;
// update ui text
$('#purchaseRequestName').text(purchaseRequestName);
// rebind the related grid
//alert(purchaseRequestID);
//location.href = "/PurchaseOrder/Index/" + purchaseRequestID;
detailsGrid.rebind();
}
function onDataBinding(e) {
e.data = $.extend(e.data, { purchaseRequestID: purchaseRequestID });
}
below is the code in the controller:
[HttpPost]
public ActionResult Create(PurchaseOrder purchaseorder)
{
if (ModelState.IsValid)
{
HERE, I WANT TO BE ABLE TO GET AND SAVE THE SELECTED ROW IN THE VIEW
//purchaseorder.PurchaseRequestID = ViewBag.SelectedID;
db.PurchaseOrders.Add(purchaseorder);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.SupplierID = new SelectList(db.Suppliers, "SupplierID", "Name", purchaseorder.SupplierID);
ViewBag.OfficeID = new SelectList(db.Offices, "OfficeID", "OfficeName", purchaseorder.OfficeID);
return View(purchaseorder);
}
Thanks
In case some of you might encounter the same problem,
I used the HiddenFor to solve this. Please suggest if you have a better way than this.
#Html.HiddenFor(model => model.PurchaseRequestID)
and then i updated the script
<script type="text/javascript">
var purchaseRequestID;
var purchaseRequestName;
function onRowSelected(e) {
var detailsGrid = $('#grdDetails').data('tGrid');
purchaseRequestID = e.row.cells[0].innerHTML;
purchaseRequestName = e.row.cells[1].innerHTML;
// update ui text
$('#purchaseRequestName').text(purchaseRequestName);
$('#PurchaseRequestID').val(purchaseRequestID);
detailsGrid.rebind();
}
function onDataBinding(e) {
e.data = $.extend(e.data, { purchaseRequestID: purchaseRequestID });
}