Kendo MVC: Adding Edit, Delete Buttons to Grid - asp.net-mvc

Assuming I have the below table:
#Html.Kendo().Grid(Model).Name("Staff").Columns(x =>
{
x.Bound(y => y.StaffId);
x.Bound(y => y.FirstName);
x.Bound(y => y.LastName);
x.Bound(y => y.Email);
x.Bound(y => y.Phone);
x.Command(y => y.Custom("Edit").Action("edit", "controller", new { id = ????? }));
}).Sortable().Scrollable().Pageable(x=> x.PageSizes(true)).Filterable()
How can I pass the primary key value (StaffId in this case) associated to the row to the object route values similar to the way it is done by Visual Studio auto-scaffold?

I do not know if you can pass ID using Command.Custom the way you are attempting right now.
If you prefer this way, you can define a JS method and fetch selected row in that and perform an AJAX operation to manipulate the data.
So in your case you can instead define command as:
columns.Command(command => command.Custom("Edit").Click("editRow"));
and in script tag, you can define method that read and send data to server:
function editRow(e) {
e.preventDefault();
try {
var dataItem = this.dataItem($(e.currentTarget).closest("tr"));
var searchId = dataItem.Id;
var searchName = dataItem.Name;
var model = { searchId: searchId };
$.ajax({
url: '#Url.Action("BindLeftMenu")',
contentType: 'application/json; charset=utf-8',
type: 'POST',
dataType: 'html',
data: JSON.stringify(model)
})
.success(function (resultMenu) {
$("#driversummaryarea").show();
})
.error(function (xhr, status) {
$("div.overlay").hide();
});
}
catch (e) {
alert(e.message);
}
}
Now there are 2 other ways you can modify grid data:
Use default commands e.g. Edit(). A demo is shown here. Simple and easy but limiting i.e. less control from JS.
Use ClientTemplate - Very powerful as offers full control over display and capturing data in JS.
For example with ClientTemplate you can define a grid like below. Notice how we are passing model Id parameter in ClientTemplate as raw html.
Once you define ClientTemplate, you can define the JS functions fnEditUser as shown above and perform operation on grid data.
HTML
#(Html.Kendo().Grid<Eda.RDBI.Web.Models.OrganizationUserViewModel>()
.Name("organizationUserViewModelGrid")
.Columns(columns =>
{
columns.Bound(p => p.FirstName).Filterable(true).Title("Name").Groupable(false).ClientTemplate("<a class='lnkEditUser' href='javascript:void(0)' onclick='fnEditUser(#=Id#)' > #=FirstName# </a>").Width(200);
columns.Bound(p => p.EMail).Width(200);
columns.Bound(p => p.Role)
.ClientTemplate("<span>#=Role#</span> <span>#=IsDriverSearchAllowed ? ' (DS)' : ''#</span>");
columns.Bound(p => p.IsActive).Title("Active")
.ClientTemplate("<input type='checkbox' #=IsActive ? checked='checked':'' # class='chkbx' onclick='return false'/>");
columns.Bound(p => p.Id).Title(string.Empty).ClientTemplate("<a class='btn btn-default' href='javascript:void(0)' onclick='fnDeleteUser(#=Id#)'>Delete</a>").Filterable(false).Sortable(false);
})
.Sortable(sortable => sortable.AllowUnsort(false))
.Scrollable()
.Filterable()
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(50)
.Model(model => model.Id(p => p.Id))
.Read(read => read.Action("GetUsersForOrganization", "OrganizationUser"))
)
.Scrollable(scrollable => scrollable.Virtual(true))
)

This works for me. If you're not doing any AJAX, you might be able to just move the anchor into Template and not use ClientTemplate. I'm using bootstrap buttons, but you could replace that code with your own styles or the kendo styles.
This is the code that inserts your id: #= Id # - it should be a field from your Model. See http://docs.telerik.com/kendo-ui/framework/templates/overview
#Html.Kendo().Grid(Model).Name("Staff").Columns(x =>
{
x.Bound(y => y.StaffId);
x.Bound(y => y.FirstName);
x.Bound(y => y.LastName);
x.Bound(y => y.Email);
x.Bound(y => y.Phone);
x.Template(#<text></text>).Title(string.Empty).Width(40)
.ClientTemplate(#"<a href='" + Url.Action("Edit") + "?id=#= Id #' class='btn btn-info btn-xs' title='Modify this'>Edit</a>");
}).Sortable().Scrollable().Pageable(x=> x.PageSizes(true)).Filterable()

After playing with the grid over and over, I could finally solve the problem. Here you go:
x.Bound(y => y.Title).Template(y=> "Click Me")

Related

Dropdown list in ASP MVC Kendo grid will not open

I am trying to replicate exactly what is on this Kendo demo page with the Categories column. Simply a drop down list inside the grid. I copied all their source code exactly, but when I click the column with a drop down, the grid simply flashes. I can edit the FullName value fine. I tried to get an animation of the effect with a screen recorder, but for some reason it is not showing everything I see. In this image I am clicking the "Rol" drop down column multiple times.
What the gif does not show is that when I click the column, it briefly changes to a drop down control for a split second. I was able to capture this by adding a "debugger" line to the onDataBound event for the dropdown and taking a screenshot:
What is going on? Why doesn't it just pop open?
Here is some of the relevant code:
The EditorTemplates/RolList.cshtml file. Note that NONE of these events fire except the onDataBound event. That is probably a clue.
#model MyCompany.Web.CaseLink.Mvc.Admin.Models.RolDropDown
#(Html.Kendo().DropDownListFor(m => m)
.BindTo((System.Collections.IEnumerable)ViewData["roles"])
.DataValueField("RolId")
.DataTextField("RolDescription")
.Events(events => events
.Select("onSelect")
.Change("ddlChange")
.Open("onOpen")
.DataBound("onDatabound")
)
)
<script type="text/javascript">
function onSelect(e) {
console.log("ddl select" + e);
}
function ddlChange(e) {
console.log("ddl change" + e);
}
function onOpen(e) {
console.log("ddl open" + e);
}
function onDatabound(e) {
debugger;
console.log("ddl databound" + e);
}
</script>
Here is the partial view containing the MVC Grid itself. Note that the gridChange event was added just for debugging, and it does not fire when clicking in the drop down column.
#(Html.Kendo().Grid<CaseMemberGridRow>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.FullName)
.Title("Name")
;
columns.Bound(p => p.Rol).ClientTemplate("#=Rol.RolDescription#").Width(180);
columns.Bound(c => c.Email)
.Title("Email")
;
columns.Bound(c => c.Telephone)
.Title("Phone")
;
columns.Command(command => command.Destroy()).Width(150);
})
.ToolBar(toolBar =>
{
toolBar.Create();
toolBar.Save();
})
.HtmlAttributes(new { style = "height: 550px;" })
.Scrollable()
.Sortable()
.Editable(editable => editable.Mode(GridEditMode.InCell))
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.ServerOperation(false)
.Events(events => events.Error("error_handler").Change("gridChange"))
.Model(m =>
{
m.Field(p => p.FullName).Editable(true);
m.Field(p => p.Rol).Editable(true);
m.Field(p => p.Rol).DefaultValue(
ViewData["defaultRol"] as MyCompany.Web.CaseLink.Mvc.Admin.Models.RolDropDown);
})
.Create(create => create.Action("AddCaseMemberNoCustomerRecord_Create", "AddMember"))
.Update(update => update.Action("AddCaseMemberNoCustomerRecord_Update", "AddMember"))
)
)
Controller is nothing special, as I said, I am doing exactly when Telerik did in their demo. A sample:
var roles = _rolService.GetAllRolesDTO()
.OrderBy(o => o.Description)
.Select(r => new RolDropDown
{
RolDescription = r.Description,
RolId = r.RolId
});
ViewData["roles"] = roles;
ViewData["defaultRol"] = roles.First();
If there is something to look for in the debugger on the "onDataBound" event, let me know. As you can see here, the datasource is definitely populated.
EDIT: I am aware that you can attempt to do the same thing using the ForeignKey type of column. When I try that, I can get a drop down to work once. After selecting a value, the column starts acting like before where you cannot change the value.
columns.ForeignKey(p => p.RolId, (System.Collections.IEnumerable)ViewData["roles"], "RolId", "Description")
.Title("Role")
.Width(150);

Kendo grid prevent server side filtering?

I have simple Kendo-Grid configuration as below (Mvc project)
#(Html.Kendo().Grid<ReviewModel>()
.Name("grid")
.DataSource(dataSource => dataSource
.Custom()
.PageSize(20)
.Schema(schema => schema.Model(m => m.Id(p => p.Id)))
.Transport(transport =>
{
transport.Read("ReadData");
})
) .Columns(columns =>
{
columns.Bound(m => m.Id).Visible(false);
columns.Bound(m => m.Code).Width(110).Filterable(ftb => ftb.Cell(cell =>
{
cell.Operator("contains");
cell.ShowOperators(false);
}));
columns.Bound(m => m.Name).Filterable(ftb => ftb.Cell(cell =>
{
cell.Operator("contains");
cell.ShowOperators(false);
}));})
.Filterable(ftb => ftb.Mode(GridFilterMode.Row))
.Pageable(p => p.Enabled(true).Info(false).Input(true).Numeric(false)))
Also do little ajax call for loading data
function ReadData(options) {
var option = {
url: '/read',
dataType: 'json',
data: {
id: '44',
type: '5'
}
};
$.ajax(option).success(function (data) {
options.success(data);
}).error(function (e) {
console.debug(e);
options.error(e);
});
}
As result everything work perfectly. grid loaded on page(Mvc partial), and then get data from server(Mvc action) and show it well.
But for first time (in each column) if user change filter of column grid call read again, and ajax call occurred. I want to prevent to call server for filtering or other things? is that possible?

kendo ui "Invalid template" error in a column client template within a detail template

I have a grid with a client detail template. This detail template is rendered in a separate partial view. Within this grid I need to use client templates to format some of the columns. Unfortunately I run into an invalid template error.
This is my detail template:
<script id="operationDetailTemplate" type="text/kendo-template">
#(Html.Kendo().TabStrip()
.Name(componentName: "tabStrip_#=Id#")
.SelectedIndex(index: 0)
.Animation(animation => animation.Open(open => open.Fade(FadeDirection.In)))
.Items(items => {
items.Add().Text(this.LocalResources(key: "Messages")).Content(#<text>
#(Html.Kendo().Grid<NotificationViewModel>()
.Name(componentName: "notificationGrid_#=Id#")
.Pageable()
.Scrollable()
.Filterable()
.Sortable()
.Columns(col => {
col.Bound(c => c.DateCreated).Format(value: "{0:yyyy/MM/dd HH:mm}");
col.Bound(c => c.Severity);
col.Bound(c => c.Title);
col.Bound(c => c.Text).ClientTemplate(string.Format("{0}...", "#= formatter(Text, 20) #"));
})
.DataSource(ds => ds.Ajax().Sort(sort => sort.Add(memberName: "DateCreated").Descending())
.Read(read => read.Route(MessageEventControllerRoute.PostReadForOperation, new RouteValueDictionary { { "operationId", "#=Id#" }, { "culture", UICulture.ToLower() } })))
.ToClientTemplate())
</text>
);
items.Add().Content(#<text>
#(Html.Kendo().Grid<OperationViewModel>().Name(componentName:"alternativesGrid")
.Columns(col => {
col.Bound(op => op.OperationIdFormatted);
col.Bound(op => op.EfficiencyRank);
col.Bound(op => op.WorkOrder.WorkOrderIdFormatted);
col.Bound(op => op.Length)
.ClientTemplate(value: "#if (data.Length) {# #:kendo.toString(Length.Hours, '00')#:#:kendo.toString(Length.Minutes, '00')#:#:kendo.toString(Length.Seconds, '00')# #}#")
.EditorTemplateName(templateName: "TimeSpan");
})
.DataSource(ds => ds.Ajax()
.Read(read => read.Route(OperationControllerRoute.PostOperationAlternatives, new RouteValueDictionary { { "workOrderId", "#=Operation.WorkOrderId#" }, { "seqNo", "#=Operation.SequenceNumber#" }, { "culture", UICulture.ToLower() } })).Model(m => m.Id(op => op.Id)))
.ToClientTemplate()
)</text>);
})
.ToClientTemplate())
</script>
As I am not able to pass an template id to the column template, I have no idea how to avoid the invalid template error.
Any ideas how to use client templates in this scenario?
Regards
Bernd
After some more experimenting I found two solutions.
Escape the client template in the low level grid.
Using
.ClientTemplate(value: "\\#if (data.Length) {\\# \\#:kendo.toString(Length.Hours, '00')\\#:\\#:kendo.toString(Length.Minutes, '00')\\#:\\#:kendo.toString(Length.Seconds, '00')\\# \\#}\\#")
instead of
.ClientTemplate(value: "#if (data.Length) {# #:kendo.toString(Length.Hours, '00')#:#:kendo.toString(Length.Minutes, '00')#:#:kendo.toString(Length.Seconds, '00')# #}#")
Change the tab content to load on demand.
Only problem here is that the ajax data source isn't called. So pass a model to the partial view and remove the
.ToClientTemplate()
instruction.

kendoui ClientTemplate in Grid not working in asp.net mvc 4

I have been looking all over for the answer and think I am missing something simple. I have a kendo grid where I want one of the columns to be a link to another page with the id as a route parameter. However, the value in the column cells are the bound values and are unchanged by my template. Any insights to this will be appreciated.
#(Html.Kendo().Grid((IEnumerable<ProviderAccess>)Model.Providers)
.Name("grants-grid")
.Columns(columns =>
{
columns.Bound(a => a.ProviderName);
columns.Bound(a => a.HasAccess);
columns.Bound(a => a.ProviderId).ClientTemplate("#= toggleLink(data) #");
})
.Scrollable()
)
<script>
function toggleLink(access) {
var action = '#Url.Action("Toggle", "Access")';
var html = kendo.format("<a href='{0}/{1}'>Toggle...</a>",
action,
access.ProviderId
);
return html;
}
</script>
ClientTemplate isn't using when Kendo Grid is binded to a dataSource on server side like your code.
You should use Template method of columns like below
columns.Template(p => "<a href='..../Toggle/Access/" + p.ProviderId + "'>Click</a>");
dataSource.Server() will let you use a custom.template
dataSource.Ajax() will let you use ClientTemplate
Figuring that out was really frustrating... They are not interchangeable one of the other will work depending on ajax or Server
<%: Html.Kendo().Grid((List<RadCarePlus.V2.Web.Models.GetMeSomeData>) ViewData["Mydata"])
.Name("Grid")
.Columns(columns =>
{
columns.Template(c => "<a href='ImplementationDetails?EpisodeID=" + c.EpisodeID + "'>" + c.EpisodeID + "</a>").Title("Testing").Width(140);
//columns.Bound(c => c.EpisodeID).Width(140);
columns.Bound(c => c.AuthStatus).Width(190);
columns.Bound(c => c.CPTCode).Width(100);
columns.Bound(c => c.inscarrier).Width(110);
columns.Bound(c => c.CreatedOn).Width(160);
//columns.Template(c => "<a href='ImplementationDetails?EpisodeID=" + c.EpisodeID + "'>" + c.EpisodeID + "</a>");
//columns.Template(c => c.EpisodeID).Title("Testing").ClientTemplate("<a href='ImplementationDetails?EpisodeID=#= EpisodeID#'>#= EpisodeID #</a>");
})
.Pageable(pageable=> pageable.ButtonCount(5))
.Sortable(sortable => sortable.AllowUnsort(false))
.DataSource(dataSource => dataSource.Server().PageSize(5)
)
%>

Grid into Grid Popup Editor - Passing ID parameter in sub grid

I have a Grid with Employes. There is a Edit button and the edit mode is set to Popup. In the EditorTemplate of the entity I want to edit, there is another grid that has a history of Salary with a incell or inline edit mode.
Both grids uses Ajax datasources. The problem is with the inner grid binding. The controller action feeding a Json result to the ajax call requires the ID of the employe we are editing to return the appropriate Salary history. However, Kendo UI ASP.NET MVC wrapper will render some sort of template of the editor before knowing which employee we want to edit, then it will edit it when we are requesting the popup.
How can I feed the Employe ID in the Read Ajax call?
Main Grid
#(Html.Kendo().Grid<MyProject.Business.Models.EmployeDTO>().Name("EmployeGrid")
.ToolBar(toolbar => toolbar.Create())
.Columns(cols =>
{
cols.Bound(o => o.someData).Title("Some Data");
cols.Bound(o => o.moreData).Title("More Data");
cols.Command(o =>
{
o.Edit();
o.Destroy();
}).Title(" ");
})
.Editable(editor => editor
.Mode(GridEditMode.PopUp)
.Window(window => window.Draggable().Resizable().HtmlAttributes(new { #style = "width:700px;" })))
.Sortable()
.Filterable()
.Groupable()
.DataSource(datasource => datasource
.Ajax()
.Model(model => model.Id(o => o.id))
.Read(read => read.Action("GetAll", "EmployesAjax"))
.Update(update => update.Action("Update", "EmployesAjax"))
.Create(create => create.Action("Create", "EmployesAjax"))
.Destroy(destroy => destroy.Action("Destroy", "EmployesAjax"))
)
)
Inner Grid (In Views/Shared/EditorTemplates/EmployeDTO.cshtml)
#Html.Kendo().Grid<MyProject.Business.Models.SalairyDTO>().Name("SalaryGrid")
.Columns(cols =>
{
cols.Bound(o => o.someInfo).Title("Some Info");
})
.DataSource(datasource => datasource
.Ajax()
.Model(model =>
{
model.Id(o => o.id);
model.Field(o => o.employe_id).DefaultValue(Model.id);
})
// NEED THE ID HERE
.Read(read => read.Action("GetByEmployeId", "SalairyAjax", new { id = "" }))
.Update(update => update.Action("Update", "SalairyAjax"))
.Create(create => create.Action("Create", "SalairyAjax"))
.Destroy(destroy => destroy.Action("Destroy", "SalairyAjax"))));
Basically I would suggest you to Set the AutoBind option to false of the inner Grid and use the edit event of the outer Grid to perform a read request and pass the value as additional parameter.
Here is an example:
function onEditOfEmployeGrid(e){
$('#SalaryGrid').data().kendoGrid.dataSource.read({id:e.model.EmployeeID})
}
You could simply pass the value from the grid using addtionaldata parameter within the grid. the RouteID on the (left side) can then be referenced within the Grid Popup Editor using ViewData["RouteID"]. I hope that helps
.Editable(editable => editable.Mode(GridEditMode.PopUp)
.TemplateName("busStop")
.DisplayDeleteConfirmation(true)
.Window(window => window.Modal(true).Resizable().Draggable())
.AdditionalViewData(new { RouteID = Model.RouteID }))

Resources