Using MVC 3 RTM and MvcContrib/FluentHtml version 3.0.51.0, I can't get the jQuery client side validation to work. Server side validation works fine, and returns showing the correct validation summary, etc. But the form post always tries to hit the server-side post controller action when it should have stopped on the client side to display the validation error.
Any ideas of what I'm missing? Could the latest version of MvcContrib/FluentHtml be incompatible with jQuery client validation?
Here's my code:
Web.config has:
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
Site.Master page has:
<script src="<%= Url.Content("~/Assets/JavaScript/jquery-1.4.4.min.js")%>" type="text/javascript"></script>
<script src="<%= Url.Content("~/Assets/JavaScript/jquery-ui-1.8.7.min.js")%>" type="text/javascript"></script>
<script src="<%= Url.Content("~/Assets/JavaScript/jquery.validate.min.js")%>" type="text/javascript"></script>
<script src="<%= Url.Content("~/Assets/JavaScript/jquery.validate.unobtrusive.min.js")%>" type="text/javascript"></script>
View page inherits from MvcContrib's ModelViewPage:
Here's the view page:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="ModelViewPage<MaintainReferralAgencyDetailVM>" %>
<%# Import Namespace="OASIS3G.Controllers" %>
<%# Import Namespace="OASIS3G.ViewModels" %>
<%# Import Namespace="JCDCHelper.Extension"%>
<%# Import Namespace="MvcContrib.FluentHtml" %>
<%# Import Namespace="System.Web.Mvc" %>
<%# Import Namespace="System.Web.Mvc.Html" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm()) %>
<% { %>
<h1>Referral Agency Detail</h1>
<%= Html.ValidationSummary(false) %>
<table class="NoBorder">
<tr>
<td class="NoBorder SubmitFormLeftColumn"><span class="required">* </span>Zip Code:</td>
<td class="NoBorder"><%= this.TextBox(x => x.ReferralAgency.ContactInformation.Zipcode) %> Example: 99999 or 999990000
<%= Html.ValidationMessageFor(x=>x.ReferralAgency.ContactInformation.Zipcode) %>
</td>
</tr>
</table>
<input type="submit" value="Submit" />
<% } %>
</asp:Content>
Here's the controller post action:
[HttpPost]
public ActionResult MaintainReferralAgencyDetail(MaintainReferralAgencyDetailVM userInputs)
{
// I shouldn't reach this when I submit the form with a blank Zipcode, but I do:
if (ModelState.IsValid)
{
Here's the entity object with the Required field:
[Serializable]
public class ContactInformationEO
{
public virtual Int64 AddressId { get; set; }
[Required]
public virtual string Zipcode { get; set; }
I figured it out, it's definitely MvcContrib messing it up. I originally thought MvcContrib wasn't a factor because when I had changed from MvcContrib's ModelViewPage to Mvc.ViewPage, I switched the "this.TextBox" to "Html.TextBox", it still didn't work. However, I just discovered that I need to switch the control to "Html.EditorFor" instead of "Html.TextBox".
I've googled for a few minutes and it doesnt look like MvcContrib version 3.0.51.0 supports jquery client-side validation with any equivalent of EditorFor, so I'll have to just rip it out and use the native MVC controls.
You can see an article with a complete solution of my approach here: automatically-validate-and-format-data-with-asp-net-mvc-and-jquery
Just ran headlong into this problem, looks like Scott Kirkland has a solution.
Related
I have got a page where im using the Nuget package pagedlist.mvc 3.0.18 (The latest version for MVC3). The problem is that when i'm listing the pages its makes my pages into a list of which look like
previous
1
2
3
4
next
Instead of < 1,2,3,4,5,6,7.. >
My view looks like
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IPagedList<News>>" %>
<%# Import namespace="PagedList" %>
<%# Import namespace="PagedList.Mvc" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<table>
<tr>
<td>
<% foreach (var v in Model)
{%>
<%: v.Content %><br />
<%: v.DateCreated %><br />
<%: v.Email %><br />
<%} %>
<h2>News</h2>
<%: Html.PagedListPager(Model, page => Url.Action("News", new { page }), PagedListRenderOptions.Minimal)%>
Code behind looks like
public ActionResult News(int? page)
{
List<News> products = HomeBLL.GetNewsList(); //returns IQueryable<Product> representing an unknown number of products. a thousand maybe?
var pageNumber = page ?? 1; // if no page was specified in the querystring, default to the first page (1)
var onePageOfProducts = products.ToPagedList(pageNumber, 25); // will only contain 25 products max because of the pageSize
ViewBag.OnePageOfProducts = onePageOfProducts;
return View(onePageOfProducts);
}
Any suggestions on what could be wrong? The standard CSS is included as well.
You should include PagedList.css in your View (it is located in Content folder).
It seems that the newest package of pagedlist is NOT supported for the aspx views, unless you reach a low level of this package.
Include the css file PagedList.css inside HEAD tags in your _Layout page or at the top of your viewpage.
I'm trying to pass the selected value of a select tag to an action through ajax when the onchange event is triggered but, for some reason, the action receives the parameter as null.
Here is the code of the jsp:
<%#taglib prefix="s" uri="/struts-tags" %>
<%#taglib prefix="sx" uri="/struts-dojo-tags" %>
<div id="navegacion_proyectos">
<s:select name="id_proyecto_actual" id="proyecto_actual_select" list="proyectos" listValue="titulo_proyecto" listKey="id_proyecto"/>
<s:url id="cambiar_proyecto_actual_url" value="/cambiar_proyecto_actual.action" />
<sx:bind sources="proyecto_actual_select" events="onchange" href="%{cambiar_proyecto_actual_url}" />
</div>
And here is the code of the action:
public class BLProyecto extends ActionSupport {
private String id_proyecto_actual;
public String cambiarProyectoActual() {
Map sesion = ActionContext.getContext().getSession();
sesion.put("proyecto_actual", id_proyecto_actual);
return SUCCESS;
}
public String getId_proyecto_actual() {
return id_proyecto_actual;
}
public void setId_proyecto_actual(String id_proyecto_actual) {
this.id_proyecto_actual = id_proyecto_actual;
}
}
I'm debbuging the code and the id_proyecto_actual parameter is shown as null. What do you think is the problem?
Thank you all, i found the answer. Instead of following an url when the event is triggered, i just submit a form. Like this:
<%#taglib prefix="s" uri="/struts-tags" %>
<%#taglib prefix="sx" uri="/struts-dojo-tags" %>
<div id="navegacion_proyectos">
<s:form id="cambiar_proyecto_actual_form" action="cambiar_proyecto_actual">
<s:select name="id_proyecto_actual" id="proyecto_actual_select" list="proyectos" listValue="titulo_proyecto" listKey="id_proyecto"/>
</s:form>
<sx:bind sources="proyecto_actual_select" formId="cambiar_proyecto_actual_form" targets="pantalla_principal" events="onchange" />
</div>
I realize I'm a little late to the party, but... I'm working in my first MVC project, and have been able to get a handle on most of what needs to be done. Most of the project simply reads data and pumps the data into charts. However, I have one View whose model looks like this (the parent class properties are not important here):
public class Class1 : ParentClass
{
public List<ChildClass> ChildClassList{get;set;}
}
and the ChildClass looks like this:
public class ChildClass
{
public int Property1{get;set;}
public int Property2{get;set;}
public string Property3{get;set;}
public int? ID{get;set;}
[Editable(true)]
public decimal? Property4{get;set;}
}
Now, retreiving the data is not an issue. I can loop through the list, and create a table for editing like this:
<% foreach(var g in Model.ChildClassList){%>
<tr>
<td style="text-align: right;">
<%= Html.Label(g.Property3)%>
</td>
<td>
<%=Html.TextBox(Model.ParentProperty.ToString() + "_" + g.Property2, (g.Property4.HasValue ? g.Property4.Value.ToString("C") : "$0.00"))%>
</td>
</tr>
<% }%>
After cruising through this site for the past couple of days, it dawned on me that I can validate the input on the server-side, in the POST method (there is a "Save" button at the bottom of the form), but (a)how do I get the validation error message back to the user, (b)perform the validation client-side?.
I must mention also that this view uses the values in the list to create a portion of a chart, prior to being rendered as a table.
On the server-side in the [HttpPost] action, you can check the validity of the model like this:
[HttpPost]
public ActionResult Save(Class1 model)
{
if (!ModelState.IsValid)
return View(model);
// Code to save model.
}
You also need to update your View to show the errors:
<%= Html.ValidationSummary(false, "Please fix these errors.")
<% foreach(var g in Model.ChildClassList){%>
<tr>
<td style="text-align: right;">
<%= Html.Label(g.Property3)%>
</td>
<td>
<%=Html.TextBox(Model.ParentProperty.ToString() + "_" + g.Property2, (g.Property4.HasValue ? g.Property4.Value.ToString("C") : "$0.00"))%>
<%= Html.ValidationMessageFor(model => g.Property4)
</td>
</tr>
<% }%>
If you want to enable it client-side, you need to use unobstrusive client validation, which you can do by updating your web.config:
<configuration>
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
</configuration>
Also you need the following JS libraries:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" type="text/javascript"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js" type="text/javascript"></script>
<script src="//ajax.microsoft.com/ajax/jQuery.Validate/1.7/jQuery.Validate.min.js" type="text/javascript"></script>
<script src="//ajax.aspnetcdn.com/ajax/mvc/3.0/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>
On a side note, try and avoid loops to render out your View. It's unnecessary code soup, which can be avoided by the use of editor templates.
I have 2 Actions in my controller:
// GET: /Directory/
public ActionResult Index()
{
ViewModels.DirectoryIndex model = new ViewModels.DirectoryIndex();
return View(model);
}
// POST: /Directory/Filter/[viewModel]
[HttpPost]
public ActionResult Filter(ViewModels.DirectoryIndex viewModel)
{
int distance = 0;
string state = string.Empty;
if (viewModel.SelectedDistance > 0)
distance = viewModel.SelectedDistance;
else if (viewModel.SelectedState > 0)
state = viewModel.States.Where(a => a.Value == viewModel.SelectedState.ToString()).FirstOrDefault().Text;
// TODO: Add filter activities here...
return PartialView("IndexPartial", viewModel);
}
I have a View and a PartialView (NOTE: I am not using Razor!)
The view looks like:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<RainWorx.FrameWorx.MVC.ViewModels.DirectoryIndex>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<div class="Column12">
<div class="Shadow"></div>
<h2 class="h2row"><%= Model.PageTitle %></h2>
<% using (Ajax.BeginForm("Filter", new AjaxOptions { UpdateTargetId = "filteredList" })) { %>
<div class="searchDirectory">
<label title="State">State: </label>
<%= Html.DropDownListFor(a => a.SelectedState, Model.States, "-- Select One --", new { #id = "ddlStates" })%>
-or-
<label title="ZipCode">Zip Code within: </label>
<%= Html.DropDownListFor(a => a.SelectedDistance, Model.Distance, "-- Select One --", new { #id = "ddlDistance" })%>
<input type="text" id="myZip" name="myZip" />
<input type="submit" value="Filter" />
</div>
<% } %>
<div id="filteredList" class="businessCard">
<% { Html.RenderPartial("IndexPartial", Model); } %>
</div>
<div style="height: 200px;"></div>
</div>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="head" runat="server">
<link type="text/css" href="Content/css/directory.css" rel="Stylesheet" />
<script src="Scripts/jquery.unobtrusive-ajax.min.js" type="text/javascript" />
<title><%= Model.PageTitle %></title>
</asp:Content>
The PartialView looks like:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<RainWorx.FrameWorx.MVC.ViewModels.DirectoryIndex>" %>
<% foreach (var d in Model.Dealers) { %>
<div class="businessCard Outline">
<div class="businessCard Logo">
<img src="<%= Url.Content("~/Content/Images/Directory/logo01_150wide.png") %>" alt="Logo" />
</div>
<div class="businessCard Info">
<div class="businessCard Name"><%= d.Name %></div>
<div class="businessCard Address"><%= d.Address %></div>
<div class="businessCard City"><%= d.City %>, <%= d.State %> <%= d.ZipCode %></div>
<div class="businessCard Phone"><%= d.Phone %></div>
</div>
</div>
<% } %>
Now, for some reason, when I select a filter to use and submit the form it does call into the second action correctly. However, it does not refresh the PartialView, instead I get the PartailView rendered as a full view. In URL terms:
I start at http://mysite.com/Directory - select my filter, click submit.
I end at http://mysite.com/Directory/Filter when I expect to end at http://mysite.com/Directory !!
I am obviously missing something simple. I've done this before in Razor (love Razor over this mess BTW) and it all works there.
NOTE: This is a 3rd party product I am extending so don't have the luxury of converting everything to Razor.
You need to have both the following files referenced in your html. I think there is a jQuery alternative... but referencing both of these JavaScript files (which are probably already in your MVC solution) should solve your problem.
<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
is it possible to use Ajax with ASP.NET MVC 2 to reload a user control, pass along a new Model and have it update all the values that make use of this model without refreshing the rest of the site content?
Yes, and here is one way to do so:
You can call an action on a controller from ajax (jquery is what I use) and get the result. To pass data up you provide parameter values to the $.ajax() call and rendering back you just render a partial with whatever viewmodel is appropriate to your partial.
To get the content displayed you just take the HTML result passed back to your $.ajax() call and, most commonly, replace the contents of a div with your HTML result.
I got it working!
I have the following code in the Controller:
[Authorize, HttpPost]
public ActionResult UpdateDinner(FormCollection formValues)
{
if (Request.IsAjaxRequest())
{
Dinner Dinner = DinnerRepository.GetDinner(Convert.ToInt32(formValues["Date"]));
return PartialView("DeclaratieWidget", Dinner);
}
}
I have this code in my View:
<script src="<%= AppPathHelper.Url(Request.ApplicationPath, "/Scripts/MicrosoftAjax.debug.js") %>" type="text/javascript"></script>
<script src="<%= AppPathHelper.Url(Request.ApplicationPath, "/Scripts/MicrosoftMvcAjax.debug.js") %>" type="text/javascript"></script>
<% using (Ajax.BeginForm("UpdateDinner", new AjaxOptions { UpdateTargetId = "Dinner" }))
{ %>
<select id="Date" name="Date">
<option value="<%= Dinner.Dinner_ID %>"><%= Dinner.Date.ToString("dddd d MMMM") %></option>
</select>
<input type="submit" value="Delete" />
<div id="avondeten">
<% Html.RenderPartial("DeclaratieWidget", Model.Dinners[0]); %>
</div>
It works perfectly this way! :D