I've just been looking at the source code for the Telerik MVC extensions and can't for the life of me work out how the template columns work. I can get them to work just fine, but I'm trying to understand what is happening under the hood, as I think they are really useful. I've stepped through the source code but I'm still a bit confused. Here's an example from the source code's example project.
Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
columns.Template(c => {
%><img
alt="<%= c.CustomerID %>"
src="<%= Url.Content("~/Content/Grid/Customers/" + c.CustomerID + ".jpg") %>"
/><%
}).Title("Picture");
columns.Bound(c => c.ContactName).Title("Name");
columns.Bound(c => c.Phone);
})
.Sortable()
.Scrollable(scrolling => scrolling.Height(250))
.Pageable()
.Render();
The columns => {} delegate is of type Action<GridColumnFactory<T>>
where Model is IEnumerable<T> and the c=>{} delegate is of type Action<TModel> . That much I understand, the Template method on the GridColumnFactory is given an action as a parameter.
How is this action used to render the required HTML?
Thanks !!!
The magic here is that Telerik uses the fact that any content (HTML, JavaScript, etc) inside ASP.NET Web Pages is in fact translated into a Response.Write-like call by the page compiler.
For example;
<% Action action = () => { %><h1>Title</h1><% }; %>
Will be translated to something like this C# code:
Action action = () => { Response.Write("<h1>Title</h1>"); };
Once you understand this, it's no different than other methods: for each cell, Telerik simply call the Action you wrote without realizing it, which contains one or more Write calls generated by the compiler.
Related
I need to use bound column value inside razor function which is used inside ClientTemplate of Kendo UI MVC grid. Like 'AnyColumnValue' parameter below:
.ClientTemplate(#KendoHtmlFuncs.RequestingDeleteWithConfirmationLink(itemNameL, AnyColumnValue));
Full example is below:
#(Html.Kendo().Grid<DisplayOfGroupCompanyVm>()
.Name(itemNameL + "-definitions-grid")
.Columns(columns =>
{
columns.Bound(c => c.Name);
columns.Bound(c => c.DateCreated).ClientTemplate("#= kendo.toString(DateCreated,'yyyy-MM-dd')#"); //.Width(150);
columns.Bound(c => c.IsActive);
columns.Bound(c => c.CreateStatus);
columns.Template(#<text></text>)
.Width(100)
.ClientTemplate(#KendoHtmlFuncs.GetModalForRequestingEditLink(itemNameL));
columns.Template(#<text></text>)
.Width(100)
.ClientTemplate(#KendoHtmlFuncs.RequestingDeleteWithConfirmationLink(itemNameL, AnyColumnValue));
})
At the end of some digging after comments of #TheDreadPirateStephen, (thanks to him) I changed my way for a workaround. The main point in his comment is 'You can't execute server code in a ClientTemplate'. Then a workaround as below is feasible to me.
.ClientTemplate(#KendoHtmlFuncs.RequestingDeleteWithConfirmationLink(itemNameL));
I gave up to use AnyColumnValue (The actual value of a column from the dataSource bound to the grid) inside html razor function. Instead of this, I've used it inside returned script by razor function. In the razor function seen below, HasPermissionForRequestingDelete variable corresponds to AnyColumnValue and it is belong to model bound to grid.
public static string RequestingDeleteWithConfirmationLink(string itemNameL)
{
string linkFormat = #"<a {0} class='k-button'>
<i class='fa fa-trash-o'></i> Request For Delete
</a>";
string conditionalPart = #"#if(HasPermissionForRequestingDelete){#
onclick='" + itemNameL + #"_DefinitionsFunc.requestingDelete(this, #= Id #)'
#}else{#
disabled='disabled'
#}#";
return string.Format(linkFormat, conditionalPart);
}
I have a hard time figuring out why the default filtering doesn't work in one of my projects.
The grid code looks perfectly similar, controller methods too. In one of the projects it works flawlessly, in other one the "filter" button appears but is not clickable.
Any clues what might be causing it?
Client side filtering is not an option for me since the amount of data is huge.
Filterable project rendered html for the filter button:
<a class="k-grid-filter" href="javascript:void(0)" tabindex="-1"><span class="k-icon k-filter"></span></a>
Unfilterable project rendered html for the filter button:
<a class="k-grid-filter" tabindex="-1"><span class="k-icon k-filter"></span></a>
The grid signature looks identical in both projects:
#(Html.Kendo().Grid<MyModel>()
.Name("grid")
.Columns(columns =>
{
//bounding several columns, columns.Bound(x => x.Field);
})
.Filterable()
.DataSource(dataSource =>
{
dataSource
.Ajax()
.PageSize(20)
.Model(model => { model.Id(product => product.Id); })
.Read(read => read.Action("Index", "Home"))
}))
Controller:
[HttpPost]
public ActionResult Index([DataSourceRequest] DataSourceRequest request)
{
var data = repository.GetAll<MyModel>();
return Json(data.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
For anyone running into a similar problem, my problem was in the included javascript files, jquery-ui.js to be prescise. Commenting it out solved this particular grid problem. Good luck!
I have a Telerik MVC grid with a single detail view.
Inside the detail view is a telerik tabstrip. When I select the grid row to display, the detail will open up completely empty.
I have copied the tabstrip code outside the grid, changing the name of the grid to a static name and rendering the grid instead of outputting it as an htmlstring, and it displays perfectly.
I've tried registering all required javascript files, no change.
I'm pretty stumped at this point. I've cleared out the entire tabstrip so it contains nothing but text, but no change.
Here's the code which i have:
<%: Html.Telerik().Grid(Model.AccessRequests)
.Name("gridAccessRequests")
.ColumnContextMenu()
.DataKeys(d => { d.Add(a => a.Access_Request_ID).RouteKey("id");})
.DataBinding(dataBinding => dataBinding
.Ajax()
.Select("AjaxBinding", "Home")
.Insert("Insert", "Home")
.Update("Save", "Home")
.Delete("Delete", "Home"))
.ToolBar(commands => commands.Insert())
.Editable(editing => editing
.InsertRowPosition(GridInsertRowPosition.Bottom)
.Mode(GridEditMode.PopUp))
.HtmlAttributes(new { style = "width:100%" })
.Selectable()
.Sortable(sorting => sorting
.OrderBy(sortOrder => sortOrder.Add(o => o.EMPLOYEE_NAME).Ascending()))
.Filterable(filtering => filtering
.Filters(filters => filters
.Add(o => o.RECORD_YEAR).IsEqualTo(Model.CurrentYear)))
.Pageable(paging =>
paging.PageSize(15)
.Style(GridPagerStyles.NextPreviousAndNumeric)
.Position(GridPagerPosition.Both))
.ClientEvents(events =>
{
events.OnEdit("onEdit");
events.OnRowDataBound("requests_onRowDataBound");
})
.DetailView(details => details.ClientTemplate(
Html.Telerik()
.TabStrip()
.Name("TabStrip_<#=id#>")
.SelectedIndex(0)
.Items(items =>
{
items.Add().Text("Assets").Content("Assets");
items.Add().Text("Locations").Content("Locations");
})
.ToHtmlString()
))
.Columns(col =>
{
col.Bound(c => c.Access_Request_ID).Title("ID");
col.Bound(c => c.RECORD_YEAR).Title("Year");
col.Bound(c => c.VERSION_NO).Title("Version");
col.Bound(c => c.EMPLOYEE_NAME).Title("Name");
col.Command(commands =>
{
commands.Edit()
.ButtonType(GridButtonType.Image);
commands.Delete()
.ButtonType(GridButtonType.Image);
commands.Custom("Clone")// the constructor accepts the name of the custom command (used to differentiate between multiple custom commands)
.Text("Clone")
// Specify which fields from the bound data item to include in the route (pass as action method arguments)
.DataRouteValues(route => route.Add(o => o.Access_Request_ID).RouteKey("requestId"))
.Ajax(true)
// Which action method to call
.Action("CloneRequest", "Home");
}).Width(145)
.IncludeInContextMenu(false);
})%>
Here is the standalone tabstrip:
<%
Html.Telerik().TabStrip()
// Make the Name unique
.Name("TabStrip")
.SelectedIndex(0)
.Items(items =>
{
items.Add().Text("Assets").Content("Assets");
items.Add().Text("Locations").Content("Locations");
})
.Render();
%>
Any Suggestion/ Solution/Demo will be helpful.
Thanks in advance!
UPDATE
I have figured out that the tabstrips are not displaying on initial load of the grid; after hitting the grid's refresh button or paging the tabstrips display and function correctly.
It appears that there is an invalid property value error being thrown after the grid is loaded but before any databinding is completed, but I still can't seem to pinpoint the exact problem. Again, hitting the grid's refresh button or paging will successfully databind all the rows.
When the page first loads, the grid is loaded via server side binding. The tab strips are defined in the client template which is not used during server binding. When you refresh the grid, it is loaded via ajax binding and the client template is used.
You need to make the grid load initially via ajax binding so you need to use a different constructor.
Change this:
Html.Telerik().Grid(Model.AccessRequests)
to this:
Html.Telerik().Grid<AccessRequests>() //Put in the appropriate type for AccessRequests
I have the following code:
#using com.mycompany.myproject.web.ViewModels
#model IEnumerable
#{ Html.Telerik().Grid(Model)
.Name("Deducciones")
.Columns(columns => {
columns.Bound(p => p.IdDeducciones).ClientTemplate("");
columns.Bound(p => p.FechaInscripcion).Width(50);
columns.Bound(p => p.FechaFin).Width(400);
})
.DataBinding(dataBinding => dataBinding
//Ajax binding
.Ajax()
//The action method which will return JSON
.Select("DeduccionesAjax", "Empleados", new { id = ViewBag.Id })
)
.Pageable(pager => pager.PageSize(2))
.Sortable()
.Render(); }
The grid renders fine but only uses the client template when I go to page 2 for instance. On the initial load it doesn't use it.
As a workaround I added .Template(#<text><a href='#'>#item.IdDeducciones</a></text>); after the ClientTemplate and now it works on both the initial load and afterwards. However this seems strange as none of the examples or docs I've seen specify both a Template and a ClientTemplate.
Is there something I'm missing that's making the first load not come from Ajax or something similar?
Thanks in advance.
As I replied in the forum thread which you opened in the Telerik forums this is expected and documented. Client templates apply only when doing client binding (such as ajax). Templates are applied during server binding such as :
Html.Telerik().Grid(Model)
What is the way to sort Telerik's RadGrid ? I don't want to add a form server tag, and I don't want to use a user control with code behind like an example I've seen (since I think these are not true MVC solutions, am I right ?).
Please point me to an example or post example code...
Thanks in advance.
For your telerik questions you should go to http://www.telerik.com/community/forums.aspx
Their support is great and if the forums don't cut it just send a formal request, you will need to create a demo project with your problem. I have used telerik products for years and they never fail to answer your question within a few days.
The demo site is also a great source of knowledge (linked above by robert)
Guido
I am using the ASP.NET MVC open source Telerik controls. Here is an example of how I'm using the sort. It works for server control or Ajax but I've found that the Ajax grid is more touchy as far as it creating circular reference errors.
This Ajax example sorts by two columns. The logic is the same for server binding.
#(Html.Telerik().Grid(Model)
.Name("Grid")
.DataKeys(keys => keys.Add(c => c.category_id ))
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("AjaxGridSelect", "CategoryTree")
.Insert("GridInsert", "CategoryTree", new { GridEditMode.PopUp, GridButtonType.ImageAndText })
.Update("GridUpdate", "CategoryTree", new { GridEditMode.InLine, GridButtonType.ImageAndText })
.Delete("GridDelete", "CategoryTree", new { GridEditMode.InLine, GridButtonType.ImageAndText }))
.Columns(columns =>
{
columns.Bound(p => p.category_name).Width(150);
columns.Bound(p => p.status_cd).Width(100);
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.ImageAndText);
commands.Delete().ButtonType(GridButtonType.ImageAndText);
}).Width(180).Title("Commands");
})
.Editable(editing => editing.Mode(GridEditMode.InLine))
.Pageable(paging => paging.PageSize(50)
.Style(GridPagerStyles.NextPreviousAndNumeric)
.Position(GridPagerPosition.Bottom))
.Sortable(o => o.OrderBy(sortcol =>
{
sortcol.Add(a => a.category_name);
sortcol.Add(a => a.add_date);
})
.Filterable()
.Groupable()
.Selectable())