jquery client side validation only works at certain field (doesnt work at Required field) - asp.net-mvc-3

I am implementing client side validation in mvc3.
I got my form showing via jquery dialog, and submit via ajax post
I am not sure is it necessary, but i created a partial class in my Model to customize the validation:
[MetadataType(typeof (FoodMetaData))]
public partial class FOOD
{
[Bind(Exclude="FoodID")]
public class FoodMetaData
{
[ScaffoldColumn(false)]
public object FoodID { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "Please enter a name")]
public object FoodName { get; set; }
[Range(1, 200, ErrorMessage = "Please enter a valid amount")]
public object FoodAmount { get; set; }
public object StorageDate { get; set; }
public object ExpiryDate { get; set; }
Currently I only get the validation shown at the amount field if i enter a string or a number out of the range. However, If i empty the Name field, nothing happen.
This is my first try on client side validation and got no idea what is happening. Can anyone please give me some advice??
Appreciate any help, thanks...

Here's an example of how you could implement a partial form with jQuery dialog.
As always start with a view model:
public class MyViewModel
{
[Required]
public string SomeProperty { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Edit()
{
return PartialView(new MyViewModel());
}
[HttpPost]
public ActionResult Edit(MyViewModel model)
{
if (!ModelState.IsValid)
{
return PartialView(model);
}
// TODO: validation passed => process the model and return a JSON success
return Json(true);
}
}
and then a ~/Views/Home/Index.cshtml view which will contain only a link to the dialog:
#Html.ActionLink("click me for dialog", "edit", null, new { id = "showDialog" })
<div id="dialog"></div>
and a ~/Views/Home/Edit.cstml partial which will contain the form that we want to be shown in the dialog:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.SomeProperty)
#Html.EditorFor(x => x.SomeProperty)
#Html.ValidationMessageFor(x => x.SomeProperty)
<button type="submit">Save</button>
}
All that is left now is to wire up. So we import the necessary scripts:
<script src="#Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
and then write our own to make the dialog live:
$(function () {
$('#showDialog').click(function () {
$('#dialog').dialog().load(this.href, function (result) {
ajaxify(this);
});
return false;
});
});
function ajaxify(dialog) {
// we need to parse client validation rules
// because the form was injected into the DOM later as
// part of the dialog. It was not present initially
// See here for more info: http://weblogs.asp.net/imranbaloch/archive/2011/03/05/unobtrusive-client-side-validation-with-dynamic-contents-in-asp-net-mvc.aspx
$.validator.unobtrusive.parse($(dialog));
// AJAXify the form
$('form', dialog).submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result === true) {
// The controller action returned a JSON result
// inidicating the success
alert('thank you for submitting');
$(dialog).dialog('close');
} else {
// there was a validation error => we refresh the dialog
// and reajaxify it as we have now modified the DOM
dialog.html(result);
ajaxify(dialog);
}
}
});
return false;
});
}
Now you could adapt this to any view model you want with any editor templates and validation rules.

I just found out that jquery client side validation is only triggered after 1st form submission after i gone through the example here: http://weblogs.asp.net/imranbaloch/archive/2011/04/30/eagerly-performing-asp-net-mvc-3-unobtrusive-client-side-validation.aspx
A great one! It helps to solve my weird problem by editing the jquery.validate.unobtrusive(.min).js file by this:
options: { // options structure passed to jQuery Validate's validate() method
errorClass: "input-validation-error",
errorElement: "span",
errorPlacement: $.proxy(onError, form),
invalidHandler: $.proxy(onErrors, form),
messages: {},
rules: {},
success: $.proxy(onSuccess, form),
onfocusout: function (element) { $(element).valid(); }
}
Thanks for every help!

Related

Ajax call returning old ASP.NET MVC partial view instead of updated view

I have an Ajax call triggered by a button, that calls a controller to update a partial view. The controller returns an updated partial view, but the partial view received in the success function of the Ajax call is the original view, not the updated view.
I created a sample ASP.NET MVC program to reproduce the problem. The program displays a list of customers in a table as follows.
Snapshot of UI
The text boxes are rendered in the partial view _Status. When the Toggle Status button is clicked, controller is called via Ajax to toggle the customer's status between true and false, and refresh the partial view of the corresponding text box. The problem is that the status never changes. Why is that?
UPDATE
I just added the following line of code in the Status action of the controller, and now, the Ajax success function correctly receives the updated partial view!
this.ModelState.Clear();
Can someone explain why?
Here is the Index.cshtml view that displays the initial view.
#model IEnumerable<ComboModel.Models.CustomerSummary>
<script type="text/javascript">
function updatePartialView(id) {
debugger;
$.ajax({
url: "/CustomerSummary/Status",
data: $('#' + id + ' :input').serialize(),
dataType: "HTML",
type: "POST",
success: function (partialView) {
// Here the received partial view is not the one created in
// the controller, but the original view. Why is that?
debugger;
$('#' + id).replaceWith(partialView);
},
error: function (err) {
debugger;
},
failure: function (err) {
debugger;
}
});
}
</script>
<h2>Customer Summary</h2>
<table>
<tr>
<th>Name</th>
<th>Active?</th>
<th>Toggle</th>
</tr>
#foreach (var summary in Model)
{
<tr>
<td>#summary.FirstName #summary.LastName</td>
#Html.Partial("_Status", summary.Input)
<td><button type="button" name="#("S" + summary.Input.Number)" onclick="updatePartialView(this.name)">Toggle Status</button></td>
</tr>
}
</table>
The _Status.cshtml partial view.
#model ComboModel.Models.CustomerSummary.CustomerSummaryInput
<td id="#("S" + Model.Number)">
#Html.TextBoxFor(model => model.Active)
<input type="hidden" value="#Model.Number" name="Number" />
</td>
The CustomerSummaryController.cs.
using System.Collections.Generic;
using System.Web.Mvc;
using ComboModel.Models;
namespace ComboModel.Controllers
{
public class CustomerSummaryController : Controller
{
private readonly CustomerSummaries _customerSummaries = new CustomerSummaries();
public ViewResult Index()
{
IEnumerable<CustomerSummary> summaries = _customerSummaries.GetAll();
return View(summaries);
}
public PartialViewResult Status(CustomerSummary.CustomerSummaryInput input)
{
this.ModelState.Clear(); // If I add this, things work. Why?
input.Active = input.Active == "true" ? "false" : "true";
return PartialView("_Status", input);
}
}
public class CustomerSummaries
{
public IEnumerable<CustomerSummary> GetAll()
{
return new[]
{
new CustomerSummary
{
Input = new CustomerSummary.CustomerSummaryInput {Active = "true", Number = 0},
FirstName = "John",
LastName = "Smith"
},
new CustomerSummary
{
Input = new CustomerSummary.CustomerSummaryInput {Active = "false", Number = 1},
FirstName = "Susan",
LastName = "Power"
},
new CustomerSummary
{
Input = new CustomerSummary.CustomerSummaryInput {Active = "true", Number = 2},
FirstName = "Jim",
LastName = "Doe"
},
};
}
}
}
And finally, the CustomerSummary.cs model.
namespace ComboModel.Models
{
public class CustomerSummary
{
public string FirstName { get; set; }
public string LastName { get; set; }
public CustomerSummaryInput Input { get; set; }
public class CustomerSummaryInput
{
public int Number { get; set; }
public string Active { get; set; }
}
}
}
Thanks!
This is a duplicate of Asp.net MVC ModelState.Clear.
In the current scenario, because there is no validation of the status field, it is ok to clear the ModelState. However, the MVC correct way would be to pass a status value (true or false) to a GET action in the controller, toggle the value, return the result string, and update the text box on the page.

Reload partial view via Ajax: controls in partial are renamed

I'm following this standard pattern for using Ajax to reload a partial view. However, when the partial view is reloaded, the controls in the view have different IDs. They lose the name of the parent model. This means that when the form is posted back, model binding won't work.
So in the example below, when the page is first loaded, the checkbox id is "PenguinEnclosure_IsEnoughPenguins" but after the partial is reloaded, the checkbox id is "IsEnoughPenguins" The ID must be "PenguinEnclosure_IsEnoughPenguins" for model binding to correctly bind this to the PenguinEnclosure property of the VM.
Model:
public class ZooViewModel
{
public string Name { get; set; }
public PenguinEnclosureVM PenguinEnclosure { get; set; }
}
public class PenguinEnclosureVM
{
public int PenguinCount { get; set; }
[Display(Name = "Is that enough penguins for you?")]
public bool IsEnoughPenguins { get; set; }
}
Controller:
public ActionResult Index()
{
var vm = new ZooViewModel
{
Name = "Chester Zoo",
PenguinEnclosure = new PenguinEnclosureVM { PenguinCount = 7 }
};
return View(vm);
}
public ActionResult UpdatePenguinEnclosure(int penguinFactor)
{
return PartialView("DisplayTemplates/PenguinEnclosureVM", new PenguinEnclosureVM { PenguinCount = penguinFactor * 10 });
}
View:
#model PartialProblem.Models.ZooViewModel
#Scripts.Render("~/bundles/jquery")
<p>
Welcome to #Model.Name !
</p>
<p>
<div id="penguin">
#Html.DisplayFor(m => m.PenguinEnclosure)
</div>
</p>
<button id="refresh">Refresh</button>
<script>
$(document).ready(function () {
$("#refresh").on("click", function () {
$.ajax({
url: "/Home/UpdatePenguinEnclosure",
type: "GET",
data: { penguinFactor: 42 }
})
.done(function (partialViewResult) {
$("#penguin").html(partialViewResult);
});
});
});
</script>
Partial View:
#model PartialProblem.Models.PenguinEnclosureVM
<p>
We have #Model.PenguinCount penguins
</p>
<p>
#Html.LabelFor(m => m.IsEnoughPenguins)
#Html.CheckBoxFor(m => m.IsEnoughPenguins)
</p>
An approach I have used is to set the "ViewData.TemplateInfo.HtmlFieldPrefix" property in the action that responds to your ajax call (UpdatePenguinEnclosure). This tells Razor to prefix your controls names and/or Ids.
You can choose whether to hard code the HtmlFieldPrefix, or pass it to the action in the ajax call. I tend to do the latter. For example, add a hidden input on the page with its value:
<input type="hidden" id="FieldPrefix" value="#ViewData.TemplateInfo.HtmlFieldPrefix" />
Access this in your ajax call:
$.ajax({
url: "/Home/UpdatePenguinEnclosure",
type: "GET",
data: { penguinFactor: 42, fieldPrefix: $("#FieldPrefix").val() }
})
Then in your action:
public ActionResult UpdatePenguinEnclosure(int penguinFactor, string fieldPrefix)
{
ViewData.TemplateInfo.HtmlFieldPrefix = fieldPrefix;
return PartialView("DisplayTemplates/PenguinEnclosureVM", new PenguinEnclosureVM { PenguinCount = penguinFactor * 10 });
}
Try this:
Controller:
public ActionResult UpdatePenguinEnclosure(int penguinFactor)
{
PenguinEnclosureVM pg = new PenguinEnclosureVM { PenguinCount = penguinFactor * 10 };
return PartialView("DisplayTemplates/UpdatePenguinEnclosure", new ZooViewModel { PenguinEnclosure = pg });
}
Your Partial:
#model PartialProblem.Models.ZooViewModel
<p>
We have #Model.PenguinEnclosure.PenguinCount penguins
</p>
<p>
#Html.LabelFor(m => m.PenguinEnclosure.IsEnoughPenguins)
#Html.CheckBoxFor(m => m.PenguinEnclosure.IsEnoughPenguins)
</p>
I Think this will do the trick

MVC Knockout validation display and

I am using knockout for the first time and I am struggling to get my head around a problem.
I have a page with multiple sections and want to be able to edit a section and submit to the controller, then display the saved details.
Each section is a partial view which contains the display information and the form. They are shown and hidden as required. I have the code working for submitting, but the problem is when the ModelState is not valid. I need to return to the form with the validation message displayed
How can I display the form again when the server validation fails? When the validation fails it currently goes back to the display section.
Also I have noticed the validation message does not display.
I am sure this must be a common problem with a simple fix. I know there are knockout validation tools, but will need to do more complex business logic validation later on and need to get the technique working.
ViewModel:
[Required]
public DateTime? InterviewDate { get; set; }
View:
<div data-bind="if: showAdminInterviewDisplay" id="Display">
<div>
<button data-bind="click: showInterviewForm" id="EditButton">Edit</button>
</div>
<div>
#Html.Label("Inteview Date") :
<label data-bind="text: interviewDate"></label>
</div>
</div>
<div data-bind="if: showAdminInterviewForm" id="Form">
<div>
#Html.Label("Interview Date")
<input data-bind="value: interviewDate" id="interviewDatePicker" />
#Html.ValidationMessageFor(m => m.InterviewDate)
</div>
<div>
<button data-bind="click: saveInterviewDate">Submit</button>
</div>
Knockout ViewModel:
function InterviewViewModel() {
//Data
var self = this;
var jsonDate = #Html.Raw(Json.Encode(#Model.InterviewDate));
var date = new Date(parseInt(jsonDate.substr(6)));
self.interviewDate = ko.observable(dateFormat(date, "dd/mm/yyyy"));
self.showAdminInterviewDisplay = ko.observable(true);
self.showAdminInterviewForm = ko.observable();
self.showInterviewForm = function () {
self.showAdminInterviewDisplay(false);
self.showAdminInterviewForm(true);
$("#interviewDatePicker").datepicker({dateFormat: 'dd/mm/yy'});
};
//Operations
self.saveInterviewDate = function() {
$.ajax("#Url.Action("SaveInterview")", {
data: ko.toJSON(self),
type: "post",
contentType: "application/json",
success: function(data) {
self.showAdminInterviewDisplay(true);
self.showAdminInterviewForm(false);
}
});
};
};
ko.applyBindings(new InterviewViewModel());
Controller:
public ActionResult SaveInterview(KnockoutViewModel model)
{
if (ModelState.IsValid)
{
return Json(model);
}
return PartialView("_AdminInterview", model);
}
Instead of returning a Partial View from your Action Method, return a serialised error model to the success function in the AJAX call. The error model will contain all the errors in the ModelState.
See this post on how to get and consume the errors from Model State:
ASP.NET MVC How to convert ModelState errors to json (JK's answer)
So you would have something like:
Error Model:
public class JsonErrorModel
{
public JsonErrorModel()
{
HasFailed = true;
}
public bool HasFailed { get; set; }
public IEnumerable ErrorList { get; set; }
}
Controller:
if(ModelState.IsValid)
{
//Do whatever here
return Json(new { model });
}
return Json(new JsonErrorModel {ErrorList = ModelState.Errors()});
Success function of AJAX call:
success: function (result) {
if(result.HasFailed) {
self.showAdminInterviewDisplay(false);
self.showAdminInterviewForm(true);
DisplayErrors(result.ErrorList);
}
else {
self.showAdminInterviewDisplay(true);
self.showAdminInterviewForm(false);
}
}
So now, if the server side validation failed, the view will show the form and the validation errors.

MVC - Jquery UI - Dialog - Question

I'm trying to throw up a dialog window in MVC without using an AjaxActionLink. I'm using jquery ui 1.8.13 in this example.
Please consider the following snippets:
public class ModalViewModel
{
public string Title { get; set; }
public string Description { get; set; }
}
And the controller:
private ActionResult GetVisitors(VisitorSearchViewModel model)
{
VisitorSearchResponse response =
.... Omitted for brevity
if (response.Success)
{
return View("VisitorList", response.Visitors.ToList());
}
else
{
return RedirectToAction("Error");
}
}
public PartialViewResult Error()
{
return PartialView("Modal",
new ModalViewModel()
{
Title = "Test Title",
Description = "Test Description"
});
}
And finally, the Modal shared view:
#model CraftTraxNG.Model.ViewModels.ModalViewModel
<script type="text/javascript">
$(document).ready(function () {
$("#errorMessage").dialog(
{ autoOpen: true,
title: '#Model.Title',
width: 500,
height: 100,
modal: true,
buttons:{ "OK": function () {
$(this).dialog("close"); }
}
});
});
<div id="errorMessage" style="display:none;">
#Model.Description
</div>
When an error is encountered, it'll write out the partial view contents; ie., it successfully creates the dialog window with the appropriate styles. My problem is I want render the partial view on something on the view I'm currently on. I've never used it in this fashion before. As of now, when it renders that partial view, I lose the contents of the view I'm currently on.
Typically, I can use an AjaxActionLink and just set a div tag I want the dialog to render to and either replace it or insert it after, before, etc.
In this case, I'm heading out to the service and grabbing a response server-side and if it's unsuccessful, I need a way to replace some div tag on the regular view with this partial view.
Any help is appreciated.
Thank you.
I have run into this issue and I have not yet come up with an ideal solution. Here is what I normally do.
Click Me
$(function() {
$("#loadsContentOrError").click(e){
e.preventDefault();
$.ajax({
url: '<%: Url.Action("Error") %>',
success: function(html) {
html = $(html);
if(html.find('.errorMessage').length > 0) {
$('#divToLoadContentTo").append(html);
} else {
$('#divToLoadContentTo").html(html);
}
}
});
});
});
This is pretty rough but it gives you a good starting point.

Have to double submit when using Remote attribute based validation

We have a field on our model which has a [Remote] attribute. When we store that field on a Hidden form element and then try to submit that form we have to click the submit button twice. Also interesting is that the 2nd time we click it no remote validation is occurring (so says Fiddler).
Thoughts?
Unable to repro. If the hidden field is decorated with the Remote attribute you won't be able to submit the form no matter how many times you click on the submit button if the remote function sends false.
For example:
Model:
public class MyViewModel
{
[HiddenInput(DisplayValue = false)]
[Remote("Check", "Home")]
public string Id { get; set; }
[Required]
public string Name { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel
{
Id = "1"
});
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
public ActionResult Check(string Id)
{
return Json(Id == "2", JsonRequestBehavior.AllowGet);
}
}
View:
#model AppName.Models.MyViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.EditorForModel()
<input type="submit" value="OK" />
}
Because the remote function will always return false this form cannot be submitted. If the remote function returns true a single click would be enough to submit it assuming of course that the other validation passed.

Resources