Telerik MVC Grid : Ajax DataBinding posts additional parameter? - asp.net-mvc

I've created a page which support editing multiple entities.
This page is called like: http://localhost/Personnel/EditMultiple?id=2944&id=7
On this page there is a GridView which should list these personnels in a grid.
This grid is defined like:
<% Html.Telerik().Grid<Web.Models.PersonnelMiniVM>()
.Columns(columns =>
{
columns.Bound(p => p.Name);
})
.DataBinding(d => d.Ajax().Select("_GetPersonnelByIds", "Personnel", new { personnelIds = string.Join(",", Model.PersonnelIds) }))
.Pageable(page => page.PageTo(Model.Page))
.Sortable(sorting => sorting.OrderBy(sortOrder => sortOrder.Add(p => p.Name)))
.Render();
%>
But when I look at the URL which is posted to the Personnel Controller, it's like:
http://localhost/Personnel/_GetPersonnelByIds/2944%2c7?personnelIds=7%2C2944&Personnel-size=5
What I don't understand is that why the 2944%2c7 is appended and this also gives me an 401 error when the URL gets too long.
The routing defined in Global.asax is like this:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
How to solve this ?

See this :
http://www.telerik.com/community/forums/aspnet-mvc/grid/mvc3-grid-control-ajax-bound-select-url-has-extra-route-values.aspx
Solution is to clear the id, like:
.DataBinding(d => d.Ajax().Select("_GetPersonnelByIds", "Personnel", new { id= "", personnelIds = string.Join(",", Model.PersonnelIds) }))

Related

Kendo Grid - Detail Template - DataSource Read is not firing the controller action

App type: ASP.NET MVC with Kendo framework
Problem Encountered: The detail template is not firing the controller action method. Please find the attached screenshot also. I checked it under the firebug window also, there is no ajax call to the controller action method "PublishedImportFiles_Read". This is making me crazy and annoying. No error is thrown or shown for this anomaly. I guess i am missing something very small.
Please help.
MVC - View Code
<div class="gapLeft gapBelow20" style="width:70%;">
#(Html.Kendo().Grid<ViewModels.PublishedImportViewModel>()
.Name("GridImport")
.Columns(columns =>
{
columns.Bound(p => p.TemplateGroup).Title("Template Group").Width("120px");
columns.Bound(p => p.TaxYear).Title("Tax Year").Width("70px");
columns.Bound(p => p.FileDescription).Title("Description").Width("200px");
columns.Bound(p => p.DatePublished).Title("Published Date").Format("{0:MM/dd/yyyy}").Width("100px");
columns.Bound(p => p.PublishedBy).Title("Published By").Width("100px");
})
.Scrollable()
.ClientDetailTemplateId("tplGridImport")
.Events(et => et.DataBound(fnCall.Replace("functionName", "gridImpDataBound")))
.Events(et => et.DetailInit(fnCall.Replace("functionName", "gridImpChildInit")))
.HtmlAttributes(new { style = "height:300px;" })
.DataSource(ds => ds.Ajax().ServerOperation(false)
.Read(read => { read.Action("PublishedImport_Read", "Import"); })
.Model(m => { m.Id(p => p.TemplateGroupID); })
)
)
</div>
<script id="tplGridImport" type="text/kendo-tmpl">
#(Html.Kendo().Grid<ViewModels.PublishedImportViewModel>()
.Name("GridImport_#=TemplateGroupID#")
.Columns(columns =>
{
columns.Bound(c => c.TemplateGroup).Title("Template Group").Width("120px");
columns.Bound(c => c.TaxYear).Title("Tax Year").Width("70px");
})
.DataSource(dsx => dsx.Ajax()
.Read(rd => { rd.Action("PublishedImportFiles_Read", "Import"); })
)
.ToClientTemplate()
)
</script>
Import Controller ActionResult Method:
[OutputCache(Duration = 0)]
public ActionResult PublishedImportFiles_Read([DataSourceRequest] DataSourceRequest request)
{
int groupID = 5;
IEnumerable<PublishedImportViewModel> pubVM = Enumerable.Empty<PublishedImportViewModel>();
var pubRecords = base.ScenarioMangtService.GetPublishedImportFilesByTemplateGroup(ClientID, SelectedYear, groupID);
pubVM = pubRecords.Select(s => new PublishedImportViewModel
{
ImportFileJobID = s.ImportFileJobID,
TemplateGroupID = s.TemplateGroupID,
TemplateGroup = s.TemplateGroup,
FileName = s.FileName,
FileDescription = s.FileDescription,
TaxYear = SelectedYear,
DatePublished = s.DatePublished,
PublishedBy = s.PublishedBy
});
return Json(pubVM.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
There was nothing wrong with the KendoGrid code. Strangely, there was a javascript error in another js file. And for some weird reason, it was breaking the binding of the detail template grid.
So when i commented the other broken code in another file, this grid starts working automatically.

MVC #Html.BeginForm Matches incorrect route

I am trying to present a form to my user where they can enter a start postcode, hit a button and end up on a page offering driving directions to a known postcode.
The route I want to match is defined as:
routes.MapRoute("Directions", "Directions/{Name}/{StartPostCode}-to-{DestinationPostCode}", new { controller = "Directions", action = "Index" });
I am using the following code to present the form:
#using (#Html.BeginForm("Index", "Directions", new { DestinationPostCode = Model.postcode, Name = Model.nameURLized }, FormMethod.Get))
{
<input type="text" name="StartPostCode" id="StartPostCode" class="inputGreyed" value="Enter Postcode" onclick="ToggleDefaultText(this)"/>
<input type="submit" value="Get Directions" />
}
The problem is that I end up at /Directions/Index?StartPostCode=abc123. It is missing the destination postcode and the name key value pairs. This of course means I end up with my request being processed by the wrong route. I have proved this using Phil Haacks route debugger.
I have tested going directly to /Directions/TheName/ABC123-to-DT83PX which works as expected. In fact, I tried this using the following code to build a link:
#Html.ActionLink("Directions Generated", "Index", "Directions", new { StartPostCode = "ABC123", DestinationPostCode = Model.postcode, Name = Model.nameURLized }, new { #class = "button", #title = "More details on " + Model.name })
Any help will be much appreciated.
Please do the following:
a) add the default route:
routes.MapRoute(null, "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }
b) Pass the values for DestinationPostCode and ListingName in hidden input fields.
c) Use:
#using (#Html.BeginForm("Index", "Directions")) { your code }

Telerik Grid Not Updating

I am trying to create a master/detail Telerik MVC grid with Ajax binding. When I select a detail row to update, click "Edit", edit the value, then click "Update", the value is not updated on the client side. I see in the debugger that the model is not updated when I call TryUpdateModel. Here is the view:
#(Html.Telerik().Grid<FeeSrcMstrDteViewModel>()
.Name("FeeSourceConfirmMasterGrid")
.Columns(columns =>
{
columns.Bound(m => m.FEE_SRC_NME).Width(65).Title(#FeeBuilderResource.FeeSourceName);
columns.Bound(m => m.PUBLISHING_ENTITY_NME).Width(45).Title(#FeeBuilderResource.PublishingEntity);
columns.Bound(m => m.FEE_SRC_SRVC_CATEGORY_NME).Width(55).Title(#FeeBuilderResource.ServiceTypeCategory);
columns.Bound(m => m.FEE_SRC_PUBLISHED_DTE).Width(45).Title(#FeeBuilderResource.PublishedDate).Format("{0:d}");
columns.Bound(m => m.YEAR).Width(30).Title(#FeeBuilderResource.Year);
if (Model.FeeSourceMasterDate.GEO_LOC_ID == 1 || Model.FeeSourceMasterDate.GEO_LOC_ID == 4)
{
columns.Bound(m => m.STATE).Width(50).Title("State or carrier locality");
}
else
{
columns.Bound(m => m.CARRLOC).Width(50).Title("State or carrier locality");
}
//columns.Command(commands => commands.Custom("ExportToExcel").Text("Export to Excel")
//.DataRouteValues(route => route.Add(m => m.FeeSourceMasterDate.FEE_SRC_MSTR_ID).RouteKey("FEE_SRC_MSTR_ID"))
//.Ajax(false).Action("_ConfirmMasterExportXlsAjax", "FeeSrcFlatRate", new { feeSourceServiceMasterDateId = "<#= FEE_SRC_MSTR_ID #>" }))
//.HtmlAttributes(new { style = "text-align: center" }).Width(50).Title(#FeeBuilderResource.Actions);
})
//.ClientEvents(events => events.OnSave("FeeSourceTestGrid_OnSave"))
.DetailView(details => details.ClientTemplate(
Html.Telerik().Grid<FeeSourceFlatRateCommentsViewModel>()
.Name("FeeSourceHome_<#= FEE_SRC_MSTR_ID #>")
.DataKeys(keys => keys.Add(m => m.CommentId))
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_GetFeeSourceDetailDataAjax", "FeeSrcFlatRate")
.Update("_UpdateFeeSourceDetailDataAjax", "FeeSrcFlatRate")
)
.Columns(columns =>
{
columns.Bound(m => m.CommentId).Width(200).Hidden();
columns.Bound(m => m.FeeSourceDescription).Width(200).Title("Fee source description");
columns.Bound(m => m.FeeSourceComments).Width(200).Title("Fee source comments");
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.ImageAndText);
}).Width(75);
})
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_GetFeeSourceDetailDataAjax", "FeeSrcFlatRate", new { id = "<#= FEE_SRC_DTE_ID #>" }))
.Editable(editing => editing.Mode(GridEditMode.InLine).InsertRowPosition(GridInsertRowPosition.Top))
.Footer(false)
.ToHtmlString()
))
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_GetFeeSourceDataAjax", "FeeSrcFlatRate"))
.Pageable(paging => paging.PageSize(20))
//.Scrollable(scrolling => scrolling.Height(580))
.Sortable()
)
The select methods work great, but not the update method. I would expect the model to be updated when i call TryUpdateModel in my controller, but I do not see that. Here is my controller code:
[HttpPost]
[GridAction]
public ActionResult _UpdateFeeSourceDetailDataAjax(int id)
{
FeeSourceFlatRateViewModel modelTest = new FeeSourceFlatRateViewModel();
List<FeeSourceFlatRateCommentsViewModel> commentsList = new
List<FeeSourceFlatRateCommentsViewModel>();
// get the model by id
IFeeSourceMstrRepository repo = new FeeSourceMstrRepository();
IList<FeeSrcMstrDteViewModel> modelTestList = repo.GetFeeSrcMstrDteViewModel((int)id);
FeeSrcMstrDteViewModel modelTestChange = modelTestList[0];
//Perform model binding (fill the product properties and validate it).
if (TryUpdateModel(modelTestChange))
{
var testy = modelTestChange;
// set the comments
FeeSourceFlatRateCommentsViewModel modelComments = new FeeSourceFlatRateCommentsViewModel
{
CommentId = id,
FeeSourceDescription = modelTestChange.FEE_SRC_DESC,
FeeSourceComments = modelTestChange.FEE_SRC_COMMENTS
};
commentsList.Add(modelTestChange);
// save the object
repo.SaveFeeSrcUOW(saveModel);
OperationStatus opStatus = repo.Save();
}
TempData["FeeSourceFlatRateModel"] = modelTestChange;
return View(new GridModel(commentsList));
}
What am I missing? Thanks for the help!
I got some help from the Telerik site. As it turns out, I was trying to update the incorrect model. My master grid is bound to FeeSrcMstrDteViewModel, but my detail grid, which I want to update, is bound to FeeSourceFlatRateCommentsViewModel.
The fix was to call TryUpdateModel on the FeeSourceFlatRateCommentsViewModel.

Define a Template column for Telerik MVC Grid in Razor syntax

I have the following legacy code that I would like to mimic, with all action links inside one column. However, I can't seem to get the Razor syntax right. How should I express this in Razor?
The ASPX column template is like this:
.Columns(column =>
{
column.Template(o =>
{%>
<%= Html.ActionLink("Edit", "Edit", new{ id = o.DeviceID}) %> |
<%= Html.ActionLink("Delete", "Delete", new { id = o.DeviceID })%>
<%});
I have only been able to get three separate columns using Razor without complaints about syntax etc. as below:
.Columns(columns =>
{
columns.Template(o => #Html.ActionLink("Edit", "Edit", new { id = o.ProductId })).Width(50);
columns.Template(o => #Html.ActionLink("Details", "Details", new { id = o.ProductId })).Width(50);
columns.Template(o => #Html.ActionLink("Delete", "Delete", new { id = o.ProductId })).Width(50);
How can I define one template column that contains all three action links using Razor syntax?
EDIT: In trying the following small adaptation of Mike's answer below, I get the error "Only assignment, call, increment, decrement, and new object expressions can be used as a statement":
columns.Template(o => #<text>#Html.ActionLink("Edit", "Edit", new { id = o.CampaignId }) |
#Html.ActionLink("Delete", "Delete", new { id = o.CampaignId })
</text>).Width(100);
Here is a quick sample showing both bound columns and a template column:
Sample #1 using #<text></text> syntax
#(Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
columns.Bound(m => m.UserName);
columns.Bound(m => m.Email);
columns.Template(#<text> #Html.ActionLink("Edit", "Edit", new { id = item.UserId} ) |
#Html.ActionLink("Delete", "Delete", new { id = item.UserId)
</text>).Width(100);
})
)
Sample #2 using an Action delegate
#(Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
columns.Bound(m => m.UserName);
columns.Bound(m => m.Email);
columns.Template(m => #Html.ActionLink("Edit", "Edit", new { id = m.UserId} ) + " | " +
#Html.ActionLink("Delete", "Delete", new { id = m.UserId)
).Width(100);
})
)
Hope that helps, if didn't already figure it out. :)
UPDATE - added the implicitly defined "item" parameter in code sample above. It shows how to get the Model properties within the Telerik control template.
UPDATE#2 - korchev showed the "#item.someProperty" syntax within his code sample. The # symbol is not needed in our case since we are within an extension method, but doesn't hurt to leave it for clarity.
UPDATE#3 - added Sample #2 code sample
If you are binding with ajax, the format has to look something more like this:
c.Bound(i => i.Name).ClientTemplate(#Html.ActionLink("<#= Name #>", "[Action]", "[Controller]", new { Id = "<#= Id #>" }, new { Area = "[Area]" }).ToHtmlString())
See here for more info: http://www.telerik.com/forums/kendo-mvc-grid-actionlink-column
columns.Template(#Html.ActionLink("Edit", "Edit", new {id = #item.id }));
Check ScottGu's blog posts with regard to Razor for what #item is.
columns.Command(commands => {
commands.Custom("Update").Text(Resource.Edit)
.ButtonType(GridButtonType.BareImage) .SendState(true).SendDataKeys(true).HtmlAttributes(new { id = "popUp"})
Action("Gallery_Bar_EditOrAddTranslate", "Administration").Ajax(false);
commands.Custom("Update").Text(Resource.Edit)
.ButtonType(GridButtonType.BareImage) .SendState(true).SendDataKeys(true).HtmlAttributes(new { id = "popUp"}) Action("Gallery_Bar_EditOrAddTranslate", "Administration").Ajax(false); }).Width("5%").Title(Resource.Coomand);
This will generate something like action Link
the id id = m.UserId you can show like DataKeys:
.Name("GridName") .DataKeys(key =>
{
key.Add(c => c.UserId).RouteKey("userId");
})
at the post method you will have :
public ActionResult xxx(int userId)
{
}
I had a print button I needed to put in the Header row, that I chose to put in the same column as, but above where the Update button goes. I was able to do it just fine in Razor like this:
columns.Command(command => {command.Edit(); }).Width(100).HeaderTemplate(i => #Html.ActionLink("Print Grid", "OutputAgencies", "Admin", new { #class = "k-button" }) );
This is where "Print Grid" was for display on my linkbutton, "OutputAgencies" was a method in my controller, and "AdminController" was the name of my controller.

Create serial number column in telerik mvc grid

I could create a grid with telerik mvc
<% Html.Telerik().Grid(Model)
.Name("ProductGrid")
.Columns(columns => {
columns.Bound(h => h.ProductName).Width("34%");
columns.Bound(h => h.Description).Width("60%");
columns.Bound(h => h.ProductID).Format(Html.ImageLink("Edit", "Products", new { Id = "{0}" }, "/Content/images/icon_edit.gif", "", new { Id = "edit{0}" }, null).ToString()).Encoded(false).Title("").Sortable(false).Width("3%");
columns.Bound(h => h.ProductID).Format(Html.ImageLink("Delete", "Products", new { Id = "{0}" }, "/Content/images/icon_delete.gif", "", new { onclick = string.Format("return confirm('Are you sure to delete this add on?');") },null).ToString()).Encoded(false).Title("").Sortable(false).Width("3%");
})
.EnableCustomBinding(true)
.DataBinding(databinding => databinding.Ajax().Select("_Index", "ProductGrid"))
.Pageable(settings => settings.Total((int)ViewData["TotalPages"])
.PageSize(10))
.Sortable()
.Render(); %>
but I need to add a serial number column in telerik grid.How can i do this?
I think the best way is to use template column like this.
<%
var sn = 0;
Html.Telerik().Grid(Model).Name("Grid").HtmlAttributes(new { style = "width:auto" })
.EnableCustomBinding(true)
.Columns(columns =>
{
columns.Template(c=> {%> <%: ++sn %> <% }).Title("S/N");
columns.Bound(c => c.CustomerNumber);
columns.Bound(c => c.Narration);
})
.Render(); %>
This way you don't have to be adding serial number property to your view models.
Hope this help?
I think the best way to do this is to add a SerialNumber column to your Product class which is your model and than just add another bound column to the grid like this:
columns.Bound(h => h.SerialNumber)
If you pass a List of Products you can populate the SerialNumber column like this:
List<Product> products = GetList(); // Your method to get data here
int counter = 1;
products.ForEach(x => x.SerialNumber = counter++);
If you want to support consistent numbers with paging you have to calculate yourself the initial value of the counter valuable. For this purpose you must have the current page and page size.
In MVC Web Grid
use this calculation
grid.Column(header: "#",
format: item => item.WebGrid.Rows.IndexOf(item) + 1 + Math.Round(Convert.ToDouble(grid.TotalRowCount / grid.PageCount) / grid.RowsPerPage) * grid.RowsPerPage * grid.PageIndex),

Resources