I have this class:
public class Product
{
[Required(ErrorMessage = "empty name")]
public string Name { get; set; }
[Required(ErrorMessage = "empty description")]
public string Description { get; set; }
}
and I have a ResponseProduct which is shown in view:
public class ProductInsertVM
{
public string Message = "Success";
public Product Product;
}
In my view I have this:
#using (Html.BeginForm()){
#Html.ValidationSummary(false)
#Html.TextBoxFor(m => m.Product.Description)
#Html.ValidationMessageFor(m => m.Product.Description)
}
What I want to know is why ValidationMessageFor doest'n work? !! only ValidationSummary works. If I respond with a Product, so it works.
The code you provided should work fine if the following conditions are met.
1) You have reference to the jQuery validation scripts in your view
<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>
2) On the form submit, you are calling the ModelState.IsValid property and hence validating the model passed to the action method
[HttpPost]
public ActionResult CarList(CarList model)
{
if (ModelState.IsValid)
{
//Save or whatever you want to do
}
return View(model);
}
Was missing this line in the _Lahout.cshtml
#Scripts.Render("~/bundles/jqueryval")
Complete:
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval") <------------
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
Related
This is my CreateUnit.cshtml where I set the templateId into the hidden input field with the id 'TemplateId'. When I confirm the dialog the asp.net mvc controller 'UnitController' and its action method 'Create' is executed which should pass the UnitViewModel. Inside the UnitViewModel the model binder has set the name property to the value I entered. But the templateId from the hidden field is missing.
Why this?
#model ITMS.Web.Models.UnitViewModel
#*Remote Validation*#
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
var templateId = $('#MyDialog').data('templateIdKey');
$('#TemplateId').text(templateId);
});
</script>
#using (Html.BeginForm("Create", "Unit"))
{
#Html.ValidationSummary(false)
<p class="editor-label">#Html.LabelFor(model => model.Name)</p>
<p class="editor-field">#Html.EditorFor(model => model.Name)</p>
<p class="editor-field">#Html.ValidationMessageFor(model => model.Name)</p>
#Html.HiddenFor(x => x.TemplateId)
}
[HttpPost]
public ActionResult Create(UnitViewModel unitViewModel)
{
if (ModelState.IsValid && !_dataProvider.UnitExists(unitViewModel.Name, unitViewModel.TemplateId))
{
Unit unit = Mapper.Map<UnitViewModel, Unit>(unitViewModel);
_dataProvider.AddUnit(unit);
return new JsonNetResult(new { success = true });
}
ModelState.AddModelError("Name", "This name already exists.");
return PartialView(unitViewModel);
}
public class UnitViewModel
{
[Required(ErrorMessage = "Name must not be empty.")]
[StringLength(30, ErrorMessage = "Enter max. 30 chars for a name.")]
[Remote("UnitExists", "Unit", ErrorMessage = "This name already exists.",AdditionalFields="TemplateId")]
[JsonProperty("title")]
public string Name { get; set; }
public Nullable<int> ParentId { get; set; }
[HiddenInput(DisplayValue = false)]
public int TemplateId { get; set; }
}
Instead of #Html.HiddenFor(x => x.TemplateId) in your view, use #Html.EditorFor(x => x.TemplateId) instead. The HTML will be rendered as a hidden input field.
Instead of:
$('#TemplateId').text(templateId);
I wrote this and now the TemplateId is sent with the form :)
$("[name='TemplateId']").val(templateId);
stupid from me the input field has no text ...
I have the following View code:
#using (Html.BeginForm("Login", "Press", FormMethod.Post))
{
<fieldset>
<legend>User Registration</legend>
<div>
#Html.TextBoxFor(model => model.FullName)
#Html.ValidationMessageFor(model => model.FullName)
</div>
<div>
#Html.TextBoxFor(model => model.Company)
#Html.ValidationMessageFor(model => model.Company)
</div>
<div>
#Html.TextBoxFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
<div>
#Html.CheckBoxFor(model => model.JoinMailingList)
Please check this box to recieve a seasonal look book pdf and monthly newsletter
</div>
<p>
<input type="submit" value="Proceed" />
</p>
</fieldset>
}
And here is my Model:
public class UserViewModel
{
[Required(ErrorMessage = "Please enter your name.")]
[MaxLength(100)]
public string FullName { get; set; }
[Required(ErrorMessage = "Please enter the name of your company.")]
[MaxLength(50)]
public string Company { get; set; }
[Required(ErrorMessage = "Please enter your email.")]
[DataType(DataType.EmailAddress)]
[RegularExpression(#"^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+#((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$", ErrorMessage = "Please enter a valid email address.")]
[MaxLength(255)]
public string EmailAddress { get; set; }
public bool JoinMailingList { get; set; }
}
The problem is that when I click on the 'Proceed' button, none of the validation occurs. It just posts the action with no validation performed on it? Do I have to perform this inside the Controller?
Here is my Controller code:
public class PressController : Controller
{
//
// GET: /Press
public ViewResult Index()
{
return View();
}
//
// GET: /Press/Login
public ViewResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(UserViewModel userViewModel)
{
return RedirectToAction("Index", "Press");
}
}
Make sure that the action you are posting to takes the view model as argument:
[HttpPost]
public ActionResult Press(UserViewModel model)
{
// at this stage the validation has been performed during
// the process of model binding and now you could look in the
// modelstate if the model is vaild:
if (!ModelState.IsValid)
{
// validation failed => redisplay the view so that the user
// can fix his errors.
// Note that calling ModelState.IsValid doesn't trigger any validation
return View(model);
}
// at this stage we know that validation passed => we could do some processing
// and redirect
return RedirectToAction("Success");
}
or some people also use the TryUpdateModel method which also allows you to perform model binding which triggers the validation:
[HttpPost]
public ActionResult Press()
{
var model = new UserViewModel();
// validation will be triggered at this stage and the method
// will return true or false based on the result of this validation
if (!TryUpdateModel(model))
{
// validation failed => redisplay the view so that the user
// can fix his errors.
return View(model);
}
// at this stage we know that validation passed => we could do some processing
// and redirect
return RedirectToAction("Success");
}
And if you want to enable client side validation, just make sure that the following 2 scripts are referenced in your page:
<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>
Do you have the client-validation enabled?
See "Step 3: Enabling Client-side Validation" in ScottGu's post
In the server side you must check is the model valid using
ModelState.IsValid
I am currently facing a problem with validation after dynamically adding content.
I have a view strongly typed to a model (Order). This Order can have many items. The model looks something like the following:
public class Order
{
[Key]
[HiddenInput]
public int id { get; set; }
[Display(Name = "Order Number")]
public string number { get; set; }
[Display(Name = "Order Date")]
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime date { get; set; }
[Required(ErrorMessage = "Beneficiary is required.")]
[Display(Name = "Beneficiary")]
public int beneficiary_id { get; set; }
[Display(Name = "Beneficiary")]
public Beneficiary beneficiary { get; set; }
[Display(Name = "Items")]
public List<Item> items { get; set; }
[Display(Name = "Payment Method")]
public List<PaymentMethod> payment_methods { get; set; }
}
I enter the order information and also the items for that specific order. I tried a couple of ways to add content dynamically and finally went with Steven Sanderson's way.
In my view, I have the regular Order information and then the items, where my model looks something like this:
#model trackmeMvc.Models.Model.Order
#{
ViewBag.Title = "Create";
Html.EnableClientValidation();
Html.EnableUnobtrusiveJavaScript();
}
<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>
<script src="#Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftMvcAjax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftMvcValidation.js")" type="text/javascript"></script>
#using (Html.BeginForm("Create", "Order", FormMethod.Post, new { #id = "create_order" }))
{
#Html.ValidationSummary(true, "Order creation was unsuccessful. Please correct the errors and try again.")
<div class="editor-label">
#Html.LabelFor(m => m.date)<req>*</req>
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.date, new { #id = "order_date" })<br />
#Html.ValidationMessageFor(m => m.date)
</div>
...
<script type="text/javascript">
$(document).ready(function () {
$("#addItem").click(function () {
var formData = $("#main_div").closest("form").serializeArray();
$.ajax({
url: "/IPO/BlankItemRow",
type: "POST",
//data: formData,
cache: false,
success: function (html) {
$("#editorRows").append(html);
//$.validator.uobtrusive.parseDynamicContent("form *");
//$("#editorRows").removeData("validator");
//$("#editorRows").removeData("unobtrusiveValidation");
//$.validator.unobtrusive.parse("#editorRows");
//$.validator.unobtrusive.parse("#create_ipo");
//$.validator.unobtrusive.parseDynamicContent($(this).first().closest("form"));
//$.validator.unobtrusive.parse($("#new_ipo_item"));
//$.validator.unobtrusive.parseElement($("#editorRows").find(".editRow:last").children().find("select"));
//$("#editorRows").find(".editRow:last").find("select").each(function () {
//alert($(this).attr("id"));
//$.validator.unobtrusive.parseElement($(this));
//$.validator.unobtrusive.parseDynamicContent($(this));
//$.validator.unobtrusive.parseDynamicContent($(this).attr("name"));
//});
//$("#editorRows").children().find(".editRows:last").find("*").each(function () {
// alert($(this).attr('id'));
//$.validator.unobtrusive.parseDynamicContent('input');
//});
//var form = $(this).closest("form").attr("id");
//$(form).removeData("validator");
//$(form).removeData("unobtrusiveValidation");
//$.validator.unobtrusive.parse(form);
}
});
return false;
});
});
</script>
Those are some of the things I tried, and nothing works.
I got the parseDynamicContent from Applying unobtrusive jquery validation to dynamic content in ASP.Net MVC. I tried it in every scenario I could think of, but still no luck.
I also tried the regular parse, removing validation from the form then applying it again, but still the newly added elements are not validated:
<div id="editorRows">
#foreach (var item in Model.items)
{
#Html.Partial("_NewItem", item)
}
</div>
... and my partial view would look something like this:
#model trackmeMvc.Models.Model.Item
#{
Layout = "";
Html.EnableClientValidation(true);
if (this.ViewContext.FormContext == null)
{
this.ViewContext.FormContext = new FormContext();
}
}
<div class="editRow">
#using (Html.BeginCollectionItem("order_items"))
{
#Html.DropDownListFor(m => m.item_id, #items, "None", new { #style = "width:205px;", #id = "ddlItems", #class="ddlItem", #name="ddlItemList" })
#Html.ValidationMessageFor(m => m.item_id)
...
}
</div>
So what's happening is, I have one empty item sent from the controller to the view by default, to show one empty row. That item is validated, but whatever comes after when I click add item, another row appears, from that partial, but I can't get it to validate. I tried to put the validation in the partial view, (before the document ready in the main form), and everything I read I applied, and it always ends up the same: validating the first row, and not the others. I tried the validation of Steven Sanderson done for that purpose - still no luck - even the validation for partials, found at this link
and the page that follows which is specific to partial validation...
What should I do to get this validation working?
Ok, I am going to start over with a new answer here.
Before you call $.validator.unobtrusive.parse, remove the original validator and unobtrusive validation from the form like so:
var form = $("#main_div").closest("form");
form.removeData('validator');
form.removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse(form);
This same answer is documented here.
What worked for me was to re-apply the validator after the call to load the partial view. In my case, I'm using $.post().then() but you could do something similar with a .always() callback of an AJAX call.
$.post(url, model, function (data) {
//load the partial view
$("#Partial").html(data);
}).then(function () {
$("form").each(function () { $.data($(this)[0], 'validator', false); });
$.validator.unobtrusive.parse("form");
});
This is my Model :
[Required(ErrorMessage = "Email required!")]
[Remote("EmailExists","User",ErrorMessage = "Email already")]
public virtual string Email { get; set; }
View :
#Html.TextBoxFor(x => x.Email)
#Html.ValidationMessageFor(x => x.Email)
Controller:
public ActionResult EmailExists(string Email)
{
return Json(!Email.Equals("teste#gmail.com"),
JsonRequestBehavior.AllowGet);
}
jquery.validate.min.js and jquery.validate.unobtrusive.min.js are added. And web.config is configured as well.
When I type on Email input it fires EmailExists fine. Returns true/false as well. But it nevers shows the ErrorMessage
And I get this error :
Erro: uncaught exception:
[Exception... "Cannot modify properties of a WrappedNative"
nsresult: "0x80570034 (NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN)"
location: "JS frame :: chrome://global/content/bindings/autocomplete.xml ::
onxblpopuphiding :: line 848" data: no]
Any idea?
There is nothing in your description that supposes a problem. I've created a new ASP.NET MVC 3 application using the default template, added the model:
public class MyViewModel
{
[Required(ErrorMessage = "Email required!")]
[Remote("EmailExists", "Home", ErrorMessage = "Email already")]
public string Email { get; set; }
}
updated the HomeController:
public class HomeController: Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
public ActionResult EmailExists(string Email)
{
return Json(
!Email.Equals("teste#gmail.com"),
JsonRequestBehavior.AllowGet
);
}
}
and the ~/Views/Home/Index.cshtml view:
#model AppName.Models.MyViewModel
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.validate.min.js")"></script>
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.validate.unobtrusive.min.js")"></script>
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.Email)
#Html.TextBoxFor(x => x.Email)
#Html.ValidationMessageFor(x => x.Email)
<input type="submit" value="OK" />
}
Validation fires fine and correct error messages are shown (tested with Chrome 10.0, IE9 and FireFox 4.0). So the question now is how does your scenario differs than this one?
You just need to do this:
[Required(ErrorMessage = "Email required!")]
[Remote("EmailExists","User")]
public virtual string Email { get; set; }
and
public JsonResult EmailExists(string Email)
{
string errorMessage = "Email already";
if (!Email.Equals("teste#gmail.com"))
return Json(true, JsonRequestBehavior.AllowGet);
return Json(errorMessage, JsonRequestBehavior.AllowGet);
}
I have a domain model and a view model as follows:
Domain Model:
namespace MvcApplication1.Models
{
public enum Sex { Male, Female };
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
[Required(ErrorMessage="Please select either Female or Male.")]
public Sex? Sex { get; set; }
}
}
View Model:
namespace MvcApplication1.ViewModels
{
public class HomeCreateVM
{
public HomeCreateVM()
{
}
public HomeCreateVM(Person p)
{
Person = p;
SelectList = p.Sex.GetSelectList();
}
public Person Person { get; set; }
public SelectList SelectList { get; set; }
}
}
The auxiliary extension method is defined as follows:
namespace MvcApplication1.Models
{
public static class Utilities
{
public static SelectList GetSelectList<XXX>(this XXX? obj) where XXX : struct
{
var values = from XXX x in Enum.GetValues(typeof(XXX))
select new { Text = x.ToString(), Value = x };
return new SelectList(values, "Value", "Text", obj);
}
}
}
Controller:
public ActionResult Create()
{
var p = new Person();
return View(new HomeCreateVM(p));
}
[HttpPost]
public ActionResult Create(Person hc)// the source of problem!
{
if (ModelState.IsValid)//always false!
{
TempData["status"] = hc;
return RedirectToAction("Confirm");
}
else
return View(new HomeCreateVM(hc));
}
HomeCreateVM.cshtml:
#model MvcApplication1.ViewModels.HomeCreateVM
<div>
Name: #Html.EditorFor(model => model.Person.Name)</div>
<div>
Sex: #Html.DropDownListFor(model => model.Person.Sex, Model.SelectList, "--Select--")</div>
Create View:
#model MvcApplication1.ViewModels.HomeCreateVM
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<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>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>HomeCreateVM</legend>
#Html.EditorForModel()
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Question:
There is no problem if the POST Create action method accepts a HomeCreateVM object as the argument.
However, if I change the POST Create action method argument from HomeCreateVM to Person (as shown in the code above), ModelState.IsValid always returns false.
The question is: "Is it possible to pass a ViewModel object to a Create view but only accept a DomainModel object from a POST Create action method?"
Because your view is strongly typed to the view model your form fields will look like this:
<input type="text" name="Person.Name" />
and if you want to bind correctly you need to specify the prefix:
[HttpPost]
public ActionResult Create([Bind(Prefix = "Person")]Person hc)
{
...
}