ModelState does not work properly - asp.net-mvc

I have a very simple application. Here's my view code;
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Kategori Modeli</legend>
<div class="editor-label">
Category Name
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CategoryName)
#Html.ValidationMessageFor(model => model.CategoryName)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>}
And here's my model code;
public int ID { get; set; }
[Required(ErrorMessage = "Category Name cannot be null.")]
[Range(3, 25, ErrorMessage = "Category Name must have 3-25 characters")]
public string CategoryName { get; set; }
And the Insert Code;
[HttpPost, ValidateInput(false)]
public ActionResult Insert(Category _category)
{
if (ModelState.IsValid)
{
---
}
}
ModelState.IsValid is always false even if it has 4 characters. Please help me. It always shows "Category Name must have 3-25 characters" error message.

Range does not validate a string length. It checks a numeric value is between the specified numbers.
MaxLength and MinLength should be used.

Related

Create record using Model List

I am trying to create an invoice. I have two models, Invoice & InvoiceItems.
I am able to insert using hardcoded values, but I want to be able to use TextBoxes to create an invoice on the fly. How do I insert a record that takes the data for the invoice and the dynamic data from the invoice items and inserts into both tables, using the same view? I'd like to have an add more button eventually where I can stay on the same page and keep adding items to the same invoice. You can see what I've tried so far below.
Invoice Model:
public class Invoice
{
[Key]
public int InvoiceId { get; set; }
public int ClientId { get; set; }
[Display(Name = "Amount")]
public decimal Amount { get; set; }
[Display(Name = "Invoice Creation Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime CreationDate { get; set; }
[Display(Name = "Invoice Due Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime DueDate { get; set; }
[Display(Name = "Notes")]
public string InvoiceNotes { get; set; }
public List<InvoiceDetails> InvoiceDetails { get; set; }
public List<Clients> Clients { get; set; }
}
InvoiceItem Model:
public class InvoiceDetails
{
[Key]
public int InvoiceDetailsId { get; set; }
public int InvoiceId { get; set; }
[DisplayName("Item Name")]
public string Name { get; set; }
[DisplayName("Item Note")]
public string Note { get; set; }
[DisplayName("Qty")]
public decimal? Quantity { get; set; }
[DisplayName("Rate/Hour")]
public decimal? Price { get; set; }
[DisplayName("Item Total")]
public decimal? Total { get; set; }
}
Invoice Controller:
private NovaDb _db = new NovaDb();
public ActionResult InvoiceInformation()
{
var invoice = new Invoice();
invoice.InvoiceDetails = new List<InvoiceDetails>();
return View(invoice);
}
[HttpPost]
public ActionResult InvoiceInformation(Invoice model)
{
if (ModelState.IsValid)
{
var invoices = new Invoice()
{
Amount = model.Amount,
CreationDate = model.CreationDate,
DueDate = model.DueDate,
InvoiceNotes = model.InvoiceNotes,
InvoiceId = model.InvoiceId,
ClientId = model.ClientId
};
_db.Invoices.Add(invoices);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
Invoice View:
#model NovaFinancial.Models.Invoice
#{
ViewBag.Title = "InvoiceInformation";
}
<h2>InvoiceInformation</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Invoice</legend>
#Html.HiddenFor(model => model.InvoiceId)
<div class="editor-label">
#Html.LabelFor(model => model.ClientId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ClientId)
#Html.ValidationMessageFor(model => model.ClientId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Amount)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Amount)
#Html.ValidationMessageFor(model => model.Amount)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CreationDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CreationDate)
#Html.ValidationMessageFor(model => model.CreationDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DueDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DueDate)
#Html.ValidationMessageFor(model => model.DueDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.InvoiceNotes)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.InvoiceNotes)
#Html.ValidationMessageFor(model => model.InvoiceNotes)
</div>
<div>
<table>
<tr>
<th>Name</th>
<th>Notes</th>
<th>Qty</th>
<th>Price</th>
<th>Total</th>
</tr>
#for (int i = 0; i < Model.InvoiceDetails.Count; i++)
{
#Html.HiddenFor(m=>m.InvoiceDetails[i].Name)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Note)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Quantity)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Price)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Total)
<tr>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Name) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Name)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Note) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Note)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Quantity) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Quantity)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Price) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Price)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Total) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Total)</td>
</tr>
}
</table>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
I worked out "a" solution that might not be the best, but it works. You'd still have to add to the code to make it more robust, but the general framework is there.
In your view, keep all the text boxes for the invoice details (Name, Quantity, Price) the same value for the name attribute, but do keep the id's unique. I used a bit of jQuery and JavaScript to generate extra rows as needed per a button that the user would click. For example,
<input type="text" name="Name" id="Name"> <!--first line item for Item Name-->
<input type="number" name="Quantity" id="Quantity"> <!--first for Quanitity -->
<input type="text" name="Name" id="Name2"> <!--second line item for Item Name-->
<input type="number" name="Quantity" id="Quantity2"> <!-- second for Quanitity -->
The values for the InvoiceDetail lines will pass back to the server as comma-delimited strings (better make certain that your item names don't have commas!). On the server-side,
var Names = Request["Name"]; // this would yield something like "Labor,Parts"
In the controller, you'll need to parse the strings into arrays and create the instances of your InvoiceDetail from them. I wrote a private method to split the strings and return a list of InvoiceDetail objects to the action method. The onus is on you to validate this data: both client-side and server-side need validation.
I did a few experiments. You can see all the code here: http://mefixme.blogspot.com/2014/10/aspnet-mvc-how-to-add-model-with.html
I hope that this helps you.

How to assign one variable and pass another with post in razor

I want to take some info from my model, edit one variable and pass it to post function. Here is my model:
public class TaskInputModel
{
[Required]
[Display(Name = "Input Value")]
public decimal Value { get; set; }
public long InputId { get; set; }
public MetriceModelTaskShedule[] Tasks;
}
and this is my Index.cshtml:
#model MetriceWeb.Models.TaskInputModel
#foreach (var item in Model.Tasks)
{
using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => item.Task)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Value)
#Html.ValidationMessageFor(model =>model.Value)
</div>
#Html.Hidden("Model.InputId", Model.InputId)
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
}
I'm receiving this like that:
[HttpPost]
public ActionResult Index(TaskInputModel model)
{
...
}
When I'm submitting only InputId has some value, Value is always 0. When i delete line: #Html.Hidden("Model.InputId", Model.InputId) Value is ok, but i don't know how to receive InputId. Can you tell me how can I do this?
Problem solved. I just had to use #Html.Hidden("InputId", Model.InputId) instead of #Html.Hidden("Model.InputId", Model.InputId)

Validations not show up using EF Code First with complex types

This is a continuation of this question Model class and Mapping
I had my Client class now working fine and it's defined as
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using DataAnnotationsExtensions;
using System.ComponentModel.DataAnnotations.Schema;
using System.Collections.Generic;
namespace CardNumbers.Objects
{
[ComplexType]
public class PhoneInfo
{
[DataType(DataType.PhoneNumber)]
[StringLength(10)]
[DisplayName("Phone")]
public virtual string Phone { get; set; }
[StringLength(5)]
[DisplayName("Ext")]
public virtual string Ext { get; set; }
public bool HasValue
{
get
{
return (Phone != null || Ext != null);
}
}
}
[ComplexType]
public class ContactDetail
{
//Constructor
public ContactDetail()
{
phoneInfo = new PhoneInfo();
}
[StringLength(100)]
[DisplayName("Contact Name")]
[DisplayFormat(NullDisplayText = "")]
public virtual string Contact { get; set; }
[Email]
[StringLength(100)]
[DisplayName("Email")]
public virtual string Email { get; set; }
public virtual PhoneInfo phoneInfo { get; set; }
public bool HasValue
{
get
{
return (Contact != null || Email != null || phoneInfo.HasValue);
}
}
}
/// <summary>
/// Client class (Client No, Client Name, Address, Contact1, Contact2 info, Created By, Modified By (operator and date)
/// </summary>
public class Client
{
public Client()
{
Contact1 = new ContactDetail();
Contact2 = new ContactDetail();
}
[Key]
[Column("ClientId",TypeName = "int")]
public virtual int Id { get; set; }
[Required]
[DisplayName("Client No")]
[Column("client_no", TypeName = "smallint")]
public virtual Int16 Number { get; set; }
[Required]
[Column("client_name", TypeName = "varchar")]
[DisplayName("Client Name")]
[MaxLength(30, ErrorMessage = "Client Name should not be longer than 30 characters" )]
[MinLength(3, ErrorMessage = "Client Name is too short")]
public virtual string Name { get; set; }
[DataType(DataType.MultilineText)]
public virtual string Address { get; set; }
public virtual ContactDetail Contact1 {get; set;}
public virtual ContactDetail Contact2 {get; set;}
[ForeignKey("EnteredByOperator")]
public string EnteredBy { get; set; }
[InverseProperty("ClientsEnteredBy")]
public virtual Operator EnteredByOperator { get; set; }
[ForeignKey("ModifiedByOperator")]
public string ModifiedBy { get; set; }
[InverseProperty("ClientsUpdatedBy")]
public virtual Operator ModifiedByOperator { get; set; }
[DataType(DataType.DateTime)]
[DisplayName("Created on")]
public DateTime EnteredOn { get; set; }
[DataType(DataType.DateTime)]
[DisplayName("Modified on")]
public DateTime? ModifiedOn { get; set; }
public virtual ICollection<ClientOrder> ClientOrders { get; set; }
public virtual ICollection<Reorder> Reorders { get; set; }
}
}
I mapped column names using Fluent API and I also re-defined my original "repository" classes to be very similar to defined in this tutorial http://code.msdn.microsoft.com/ASPNET-MVC-Application-b01a9fe8
This is my current partial view for the client form called _ClientForm:
#using WebDemo.Helper
#model CardNumbers.Objects.Client
<fieldset>
<legend>Client Info</legend>
#Html.ValidationSummary(true)
<input type="hidden" id="fntype" name="fntype">
#Html.HiddenFor(model => model.Id)
#Html.EditorFor(model => model.Number, EditorTemplate.TextBox)
#Html.EditorFor(model => model.Name, EditorTemplate.TextBox)
#Html.EditorFor(model => model.Address, EditorTemplate.EditBox)
<div id="ContactsInfo">
#*Contact 1*#
<div id="Contact1">
#*#Html.EditorFor(model=>model.Contact1)*#
#Html.EditorFor(model=>model.Contact1.Contact, EditorTemplate.TextBox)
#Html.EditorFor(model=>model.Contact1.Email, EditorTemplate.TextBox)
</div>
#*Contact2*#
<div id="Contact2">
#* #Html.EditorFor(model => model.Contact2)*#
</div>
</div>
#*<div class="clear"></div>*#
<div id="SaveCancel" class="float-right">
<button type="Submit" id="btnSave">Save</button>
<button type="reset" id="btnCancel">Cancel</button>
</div>
</fieldset>
I already tried to revert to original way of only one level and I also commented the second Contact2 info but still the e-mail validation doesn't work and all other validations also don't seem to work.
The EditorFor textboxes are defined based on this blog post http://fusionovation.com/post/2010/02/15/adding-a-rich-text-editor-to-asp-net-mvc-using-strongly-typed-helpers-dataannotations-amp-jquery.aspx
And these are two of the new EditorFor I added:
PhoneInfo.cshtml
#using WebDemo.Helper
#model CardNumbers.Objects.PhoneInfo
<div id="PhoneInfo">
<div class="float-left">
#Html.EditorFor(model => model.Phone, EditorTemplate.TextBox)
</div>
<div class="float-right">
#Html.EditorFor(model => model.Ext, EditorTemplate.TextBox)
</div>
</div>
And ContactDetail.cshtml
#using WebDemo.Helper
#model CardNumbers.Objects.ContactDetail
#Html.EditorFor(model => model.Contact, EditorTemplate.TextBox)
#Html.EditorFor(model => model.Email, EditorTemplate.TextBox)
#Html.EditorFor(model=>model.phoneInfo)
So, as you can see, the code of the views is now very compact.
However, with all of these in place the validations don't see to fire anymore. I used to test validation on EMail by typing some garbage. It used to provide a validation message near the textbox. Now I observe that the email textbox takes the red border, but there is no message.
Do you see what I am missing now and if it's possible to use complex type and validations?
To clarify, the _ClientForm is called from this Client view:
#model CardNumbers.Objects.Client
#{
ViewBag.Title = "Client";
}
#section scripts {
<script src="#Url.Content("~/Scripts/Clients.js")" type="text/javascript" ></script>
}
<form id="frmClientsSearch">
<label for="clientNo">Client No: </label>
<input type="number" name="searchClientNo" class="numericOnly" /><br />
<label for="clientName">Client Name: </label>
<input type = "text" size =25 value ="Please enter the search value" class="SelectOnEntry"
name ="searchClientName" />
<input type="button" id="btnClientsSearch" value ="Find / Refresh" />
</form>
<div style="padding-left: 150px; padding-top: 50px; padding-bottom: 50px;" id="ClientsResults">
<table id="flexClients" style="display: none">
</table>
</div>
<div style="display: none">
<form id="sform" title="Client Info">
#{Html.RenderPartial("_ClientForm", Model) ;}
</form>
</div>
Thanks.
I don't see a form anywhere on your page. A form context is required for validation to work. You need to wrap the Editor attributes in BeginForm block.
After some trials and error I found that the TextBox EditorFor view was the culprit. I documented what I found in my answer here http://forums.asp.net/t/1855963.aspx/1?Validation+messages+don+t+show+up+what+is+missing+
Basically, as long as I use this EditorFor
#*#using WebDemo.Helper*#
#model CardNumbers.Objects.PhoneInfo
<div id="PhoneInfo">
<div class="float-left">
#* #Html.EditorFor(model => model.Phone, EditorTemplate.TextBox)*#
<div class="editor-label">
#Html.LabelFor(model => model.Phone)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Phone)
#Html.ValidationMessageFor(model => model.Phone)
</div>
</div>
<div class="float-right">
#*#Html.EditorFor(model => model.Ext, EditorTemplate.TextBox)*#
<div class="editor-label">
#Html.LabelFor(model => model.Ext)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Ext)
#Html.ValidationMessageFor(model => model.Ext)
</div>
</div>
</div>
All seems to work OK. But if I try to switch to a shorter syntax and use this EditorFor for the textbox:
<div class="editor-label">
#Html.Label((ViewData.ModelMetadata.DisplayName??ViewData.ModelMetadata.PropertyName),
new Dictionary<string, object>
{
{ "for", ViewData.ModelMetadata.PropertyName }
})
</div>
<div class="editor-field">
#Html.TextBox("", (object)Model,
new Dictionary<string, object>
{
{ "id", ViewData.ModelMetadata.PropertyName },
{ "name", ViewData.ModelMetadata.PropertyName },
{ "class", "text-box single-line"},
{ "data-bind", "value: " + ViewData.ModelMetadata.PropertyName },
})
#Html.ValidationMessage(ViewData.ModelMetadata.PropertyName,
new Dictionary<string, object>
{
{ "data-valmsg-for", ViewData.ModelMetadata.PropertyName }
})
</div>
Validation messages do not show anymore.
Hopefully this answer will help someone or you may see what I am missing here.

Unobtrusive javascript adding data to #Html.HiddenFor

I have a hidden field on a form that is created in Razor using the #Html.HiddenFor helper:
#Html.HiddenFor(model => model.BidID, new { id="bidItemID" })
My View model looks like this:
public class BidEditVM
{
[Display(Name = "Bid ID")]
public int BidID { get; set; }
[StringLength(51)]
[Display(Name = "Customer Name")]
public string CustomerName { get; set; }
[StringLength(75)]
[Display(Name = "Bid Name")]
public string BidName { get; set; }
[Display(Name = "Amount")]
public decimal Amount { get; set; }
[Display(Name = "Time")]
public DateTime BidTime { get; set; }
}
When the HTML is rendered, unobtrusive javascript adds it's stuff to the hidden input field even though it will never require validation:
<input id="bidItemID" type="hidden" value="5198" name="BidID" data-val-required="The Bid ID field is required." data-val-number="The field Bid ID must be a number." data-val="true">
What's odder is that the message and validation it adds aren't even part of the view model for this partial view. The view looks like this:
#model AuctionAdmin.Models.ViewModels.BidEditVM
#using (Ajax.BeginForm("UpdateBid", new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "modalBidInfo" }))
{
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.BidID, new { id="bidItemID" })
<fieldset>
<legend>Edit Bid</legend>
<div class="display-label">#Html.LabelFor(model => model.CustomerName)</div>
<div class="display-field">
#Html.DisplayFor(model => model.CustomerName)
</div>
<div class="display-label">#Html.LabelFor(model => model.BidName)</div>
<div class="display-field">
#Html.DisplayFor(model => model.BidName)
</div>
<div class="editor-label">#Html.LabelFor(model => model.Amount)</div>
<div class="editor-field">
#Html.EditorFor(model => model.Amount)
</div>
<div class="editor-label">#Html.LabelFor(model => model.BidTime)</div>
<div class="editor-field">
#Html.EditorFor(model => model.BidTime)
</div>
</fieldset>
}
Where is it getting this metadata from and how can I stop it?
It's marked as such since the type in the view model is an int.
It's adding the html due to this line:
#Html.HiddenFor(model => model.BidID, new { id="bidItemID" })
Why is it a problem that the extra attributes are present?
If it really is problematic, try changing the type of BidId to int? (a nullable int).

ASP.NET MVC3 Remote Validation does not output error message

I have a simple Model that is the default RegisterModel and upon creating a View based on that model for Create I end up having
public class RegisterModel
{
[Required]
[Remote("UserNameExists", "Account", "", ErrorMessage = "Username is already taken.")]
[Display(Name = "Username (spaces will be stripped, must be at least 6 characters long)")]
public string UserName { get; set; }
[Required]
[Editable(true)]
[Display(Name = "First and Last name")]
public string Name { get; set; }
[Required]
[DataType(DataType.EmailAddress, ErrorMessage = "You need to enter a valid email")]
[Remote("EmailExists", "Account", "", ErrorMessage = "Email is already taken.")]
[Display(Name = "Email address")]
public string Email { get; set; }
//[Required]
//[ValidatePasswordLength]
[DataType(DataType.Password)]
[Display(Name = "Create a password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Verify password")]
//[Compare("Password", ErrorMessage = "Password's do not match.")]
public string ConfirmPassword { get; set; }
}
and in the View:
<h3>
Details</h3>
#using (Html.BeginForm("GenerateBetaLink", "Account", FormMethod.Post, new { #id = "beta-user" }))
{
#Html.ValidationSummary(true)
<div>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<p>
</p>
<p>
<input type="submit" value="Create Beta User" class="btn-submit" />
<span class="loading"></span>
</p>
</div>
}
My Validation Controller
public class ValidationController : Controller
{
public JsonResult UserNameExists(string UserName)
{
OnlineServicesRepository db = new OnlineServicesRepository();
var user = db.FindUserByUsername(UserName.Trim());
return user == null ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(string.Format("{0} is not available.", UserName),
JsonRequestBehavior.AllowGet);
}
public JsonResult EmailExists(string Email)
{
OnlineServicesRepository db = new OnlineServicesRepository();
var user = db.FindUserByEmail(Email.Trim());
return user != null ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(string.Format("{0} is not available.", Email),
JsonRequestBehavior.AllowGet);
}
}
My problem is that Remote Validation does fire, but does not write anything into the Error Message as it should, plus, the jQuery method .valid() keeps telling me that the form is valid:
(source: balexandre.com)
What am I missing here?
The MSDN article shows the same code (in the downloadable file)
The following worked fine for me:
Model:
public class RegisterModel
{
[Required]
[DataType(DataType.EmailAddress, ErrorMessage = "You need to enter a valid email")]
[Remote("EmailExists", "Home", "")]
[Display(Name = "Email address")]
public string Email { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(RegisterModel model)
{
return View(model);
}
public ActionResult EmailExists(string email)
{
if ((email ?? string.Empty).Contains("foo"))
{
return Json(email + " is not available", JsonRequestBehavior.AllowGet);
}
return Json(true, JsonRequestBehavior.AllowGet);
}
}
View:
#model RegisterModel
<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.LabelFor(model => model.Email)
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
<input type="submit" value="OK" />
}
I had the same problem and resolved it by updating to the latest jQuery (1.6) and jQuery.validate (1.8) libraries. The easiest way to get these is searching NuGet for jQuery.

Resources