Telerik Grid paging and Sorting in ASP.Net MVC3(aspx engine) - asp.net-mvc-3

I am using Telerik Rad Grid in MVC3(aspx engine) like:-
This is my Controller:-
[HttpGet]
[GridAction(EnableCustomBinding = true)]
public ActionResult Search()
{
SearchViewModel searchViewModel = new SearchViewModel(this.serviceInvoker);
SearchRequest searchRequest = new SearchRequest();
searchViewModel.Initialize();
ViewData["TotalRecord"] = SearchViewModel.SearchResponses.Count();
return View(searchViewModel);
}
This is my ViewModel:-
<% using (Html.BeginForm("CaseSearch", "AdvanceSearch", FormMethod.Post, new { #class "formStyle" }))
{ %>
<div class="boxPanel">
<fieldset>
<legend></legend>
<ul class="floatleft width25" >
<div class="floatleft">
<% Html.RenderPartial("PartialViewCaseSearch"); %>
</div>
</fieldset>
<% } %>
This is my Partial View Telerik Grid:-
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<%# Import Namespace="ABC.DataContracts.Messages" %>
<%= Html.Telerik().Grid((IEnumerable<SearchResponse>)Model.SearchResponses)
.Name("CaseSearchGrid")
.Columns(columns =>
{
columns.Bound(grid => grid.RowNumber);
columns.Bound(grid => grid.CreatedOn).Format("{0:dd/MM/yyyy}");
columns.Bound(grid => grid.CaseReference);
})
.DataBinding(dataBinding => dataBinding.Ajax().Select("Search", "AdvanceSearch"))
.EnableCustomBinding(true)
.BindTo((IEnumerable<SearchResponse>)Model.SearchResponses)
.Pageable(paging => paging.Enabled(true))
%>
SearchResponse is my object which is result of search request.
I have to implement Custom Server Ajax binding with Server side Paging ans Sorting. I have implemented Paging and sorting in my stored procedure. but when ever i am clicking on page index or sorting column its not returning proper data and throughing error like. " Invalid JSON request".
Please also assist me how to pass the sort column name and page index to my controller.
Thanks.

Your AdvanceSearch() action should take GridCommand as it's parameter, so you know what sorting to apply. Take a look at Telerik documentation.
You can also attack the debugger in order to see what the actual exception on the server is.

Related

Best way to pass single value from view to controller

I have a form that posts a single value, but I cant seem to get it to the controller. I have verified the value exists in the form, but it arrives at the controller as null. Here is the form post:
<%Html.BeginForm("SaveRecord", "NewApplicant", FormMethod.Post, new { id = Model.PersonModel.ApplicantID } ); %>
<%: Html.Hidden("NewId", Model.PersonModel.ApplicantID) %>
<input type="submit" class="SKButton" value="Save" title="Save this new application as a unique record." />
<% Html.EndForm(); %>
and here is the contoller action:
public ActionResult SaveRecord(NewApplicantViewModel model)
{
int NewAppId = model.PersonModel.ApplicantID;
I have also tried:
public ActionResult SaveRecord(int NewId)
{
model.PersonModel.ApplicantID = NewId;
These must be a simple fix, and I want to pass the id in the model, dont want to use ajax. Thoughts?
Try using:
<%: Html.HiddenFor(model => model.PersonModel.ApplicantID) %>

MVC3 Master-Details Validation not Displaying

I have an MVC3 page with an object (Header) that has data and a list of objects (Details) that I want to update on a single page. On the details object I have custom validation (IValidatableObject) that also needs to run.
This appears to generally be working as expected, validations are running and returning ValidationResults and if I put an #Html.ValidationSummary(false); on the page it displays those validations. However I don't want a list of validations at the top, but rather next to the item being validated i.e. Html.ValidationMessageFor which is on the page, but not displaying the relevant message. Is there something I'm missing? This is working on other pages (that don't have this Master-Details situation), so i'm thinking it is something about how I'm going about setting up the list of items to be updated or the editor template for the item?
Edit.cshtml (the Header-Details edit view)
#foreach (var d in Model.Details.OrderBy(d => d.DetailId))
{
#Html.EditorFor(item => d, "Detail")
}
Detail.ascx (the Details Editor Template)
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Detail>" %>
<tr>
<td>
<%= Model.Name %>
<%= Html.HiddenFor(model => model.DetailId) %>
</td>
<td class="colDescription">
<%= Html.EditorFor(model => model.Description) %>
<%= Html.ValidationMessageFor(model => model.Description) %>
</td>
<td class="colAmount">
<%= Html.EditorFor(model => model.Amount) %>
<%= Html.ValidationMessageFor(model => model.Amount) %>
</td>
</tr>
Model is Entity Framework with Header that has Name and HeaderId and Detail has DetailId, HeaderId, Description and Amount
Controller Code:
public ActionResult Edit(Header header, FormCollection formCollection)
{
if (formCollection["saveButton"] != null)
{
header = this.ProcessFormCollectionHeader(header, formCollection);
if (ModelState.IsValid)
{
return new RedirectResult("~/saveNotification");
}
else
{
return View("Edit", header);
}
}
else
{
return View("Edit", header);
}
}
[I know controller code can be cleaned up a bit, just at this state as a result of trying to determine what is occuring here]
IValidatableObject implementation:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (this.Name.Length < 5) && (this.Amount > 10))
{
yield return new ValidationResult("Item must have sensible name to have Amount larger than 10.", new[] { "Amount" });
}
}
I would recommend you to use real editor templates. The problem with your code is that you are writing a foreach loop inside your view to render the template which generates wrong names for the corresponding input fields. I guess that's the reason why you are doing some workarounds in your controller action to populate the model (header = this.ProcessFormCollectionHeader(header, formCollection);) instead of simply using the model binder to do the job.
So let me show you the correct way to achieve that.
Model:
public class Header
{
public IEnumerable<Detail> Details { get; set; }
}
public class Detail : IValidatableObject
{
public int DetailId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Amount { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if ((this.Name ?? string.Empty).Length < 5 && this.Amount > 10)
{
yield return new ValidationResult(
"Item must have sensible name to have Amount larger than 10.",
new[] { "Amount" }
);
}
}
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new Header
{
Details = Enumerable.Range(1, 5).Select(x => new Detail
{
DetailId = x,
Name = "n" + x,
Amount = 50
}).OrderBy(d => d.DetailId)
};
return View(model);
}
[HttpPost]
public ActionResult Index(Header model)
{
if (ModelState.IsValid)
{
return Redirect("~/saveNotification");
}
return View(model);
}
}
View (~/Views/Home/Index.cshtml):
#model Header
#using (Html.BeginForm())
{
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
#Html.EditorFor(x => x.Details)
</tbody>
</table>
<button type="submit">OK</button>
}
Editor template for the Detail type (~/Views/Shared/EditorTemplates/Detail.ascx or ~/Views/Shared/EditorTemplates/Detail.cshtml for Razor):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<MvcApplication1.Controllers.Detail>"
%>
<tr>
<td>
<%= Html.DisplayFor(model => model.Name) %>
<%= Html.HiddenFor(model => model.DetailId) %>
<%= Html.HiddenFor(model => model.Name) %>
</td>
<td class="colDescription">
<%= Html.EditorFor(model => model.Description) %>
<%= Html.ValidationMessageFor(model => model.Description) %>
</td>
<td class="colAmount">
<%= Html.EditorFor(model => model.Amount) %>
<%= Html.ValidationMessageFor(model => model.Amount) %>
</td>
</tr>
Here are a couple of things that I did to improve your code:
I performed the ordering of the Details collection by DetailId at the controller level. It's the controller's responsibility to prepare the view model for display. The view should not be doing this ordering. All that the view should do is display the data
Thanks to the previous improvement I git rid of the foreach loop in the view that you were using to render the editor template and replaced it with a single #Html.EditorFor(x => x.Details) call. The way this works is that ASP.NET MVC detects that Details is a collection property (of type IEnumerable<Detail>) and it will automatically look for a custom editor templated inside the ~/Views/SomeController/EditorTemplates or ~/Views/Shared/EditorTemplates folders called Detail.ascx or Detail.cshtml (same name as the type of the collection). It will then render this template for each element of the collection so that you don't need to worry about it
Thanks to the previous improvement, inside the [HttpPost] action you no longer need any ProcessFormCollectionHeader hacks. The header action argument will be correctly bound from the request data by the model binder
Inside the Detail.ascx template I have replaced <%= Model.Name %> with <%= Html.DisplayFor(model => model.Name) %> in order to properly HTML encode the output and fill the XSS hole that was open on your site.
Inside the Validate method I ensured that the Name property is not null before testing against its length. By the way in your example you only had an input field for the Description field inside the template and didn't have a corresponding input field for the Name property, so when the form is submitted this property will always be null. As a consequence I have added a corresponding hidden input field for it.

make Html.Editorfor field readonly in mvc3

I am using this, an editor template (located in the Shared\EditorTemplates folder in my solution)
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.DateTime?>" %>
<%=Html.TextBox("", (Model.HasValue ? Model.Value.ToString("MM/dd/yyyy") : string.Empty), ViewData )%>
and this in my view
#Html.EditorFor(model => model.ModifiedDate)
how to make this field readonly in the view
<%= Html.EditorFor(x => x.ModifiedDate, new { #readonly = "readonly" }) %>
UPDATE:
OK, now that you sent me the sample project here are the issues:
You have a spelling mistake in the ~/Views/Shared/EditorTempletes folder. It should be ~/Views/Shared/EditorTemplates.
You editor template must be called DateTime.ascx and not a DateTime.aspx. And because of this the header must look like this (use <%# Control ... instead of <%# Page ...):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<System.DateTime?>"
%>
<%= Html.TextBox(
"",
(Model.HasValue ? Model.Value.ToString("MM/dd/yyyy") : string.Empty),
ViewData
) %>
You can use:
#Html.DisplayFor()
Decorate the property with the [HiddenInput] Attribute from the System.Web.Mvc namespace.
I use to show read-only information this way:
#Html.DisplayFor(model => model.CadastradoEm, new { #class = "form-control" })
#Html.HiddenFor(model => model.CadastradoEm)
You need to include a hidden input in addition to the display text, cause DisplayFor() doesn't generate a control that posts back
;)

What's wrong with Razor markup?

I am trying to learn MVC and using samples from the "Pro ASP .net MVC 2". Only I'm trying to write everything in MVC3.
first I had some problem with #Html.RenderAction thing, I changed it to #Html.Action - it did the trick.
Now I have a problem. Could you tell me why ascx view works and similar Razor doesn't?
ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<SportsStore.WebUI.Models.NavLink>>" %>
<% foreach (var link in Model) { %>
<%: Html.RouteLink(link.Text, link.RouteValues, new Dictionary<string, object> {
{ "class", link.IsSelected ? "selected" : null }
}) %>
<% } %>
Razor:
#model IEnumerable<SportsStore.WebUI.Models.NavLink>
#foreach (var link in Model)
{
Html.RouteLink(link.Text, link.RouteValues, new Dictionary<string, object> { { "class", link.IsSelected ? "selected" : null } });
}
The Html.RouteLink method returns an IHtmlString.
It does not write anything to the page.
You need to write the returned IHtmlString to the page by writing #Html.RouteLink(...).
This is Razor's equivalent to the <%: Html.RouteLink(...) %> in the ASCX page.
Try this:
#model IEnumerable<SportsStore.WebUI.Models.NavLink>
#foreach (var link in Model)
{
#Html.RouteLink(link.Text, link.RouteValues, new { #class = link.IsSelected ? "selected" : string.empty });
}

Razor syntax to declare action that renders html

I'm trying to create an action that renders some html in the razor view engine. This was pretty easy in the webforms engine but I'm having some issues with razor. Here is the simplest form of what I'm trying to accomplish using the webforms engine:
<% var myAction = new Action<HtmlHelper<int>>((helper) => { %>
<div>
<%= helper.ViewData.Model %>
</div>
<%}); %>
The closest I've come in the razor view engine is:
#{var myAction = new Action<HtmlHelper<int>>((help) =>
{
#<div>
#help.ViewData.Model
</div>;
});
}
This gives a "CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement" error.
Any help would be appreciated. Thanks.
#{
Func<dynamic, object> myAction =
#<div>
#item.ProductName
</div>;
}
#myAction(Model)
You may also checkout the following blog post.
UPDATE:
You may also do this:
#{
Func<HtmlHelper<int>, object> myAction = #<div>#item.ViewData.Model</div>;
}
or:
#{
Func<dynamic, object> myAction = #<div>#item.ViewData.Model</div>;
}
and to invoke:
#myAction(someInstanceOfTheRequiredHelper)

Resources