Unrequired property keeps getting data-val-required attribute - asp.net-mvc

This is the model with it's validation:
[MetadataType(typeof(TagValidation))]
public partial class Tag
{
}
public class TagValidation
{
[Editable(false)]
[HiddenInput(DisplayValue = false)]
public int TagId { get; set; }
[Required]
[StringLength(20)]
[DataType(DataType.Text)]
public string Name { get; set; }
//...
}
Here is the view:
<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>Tag</legend>
<div>#Html.EditorForModel()</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
And here is what get's renderd:
<form action="/Tag/Create" method="post">
<fieldset>
<legend>Tag</legend>
<div><input data-val="true" data-val-number="The field TagId must be a number." data-val-required="The TagId field is required." id="TagId" name="TagId" type="hidden" value="" />
<div class="editor-label"><label for="Name">Name</label></div>
<div class="editor-field"><input class="text-box single-line" data-val="true" data-val-length="The field Name must be a string with a maximum length of 20." data-val-length-max="20" data-val-required="The Name field is required." id="Name" name="Name" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span></div>
...
</fieldset>
</form>
The problem is that TagId validation gets generated althoug thare is no Required attribute set on TagId property. Because of that I can't even pass the client-side validation in order to create new Tag in db.
What am I missing?

I found the answer. Just add this to Application_Start:
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

Make the view-model value-types nullable. Then they won't be Required by default.
Note also if you put the attribute 'required="false"' in html 5 (if you set html 5 in your doctype meta data), it will see "required" and make it required. You can use dojo-data-props="required:false".

frennky's solution only removed data-val-required but in my case I still had data-val-number and data-val
I had to add the two lines below to Application_Start to get rid of everything.
ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new DataAnnotationsModelValidatorProvider());

The problem is that the value of the hidden field is empty. This shouldn't happen if you use integer type. I suppose that the TagId property is defined as a nullable type in the Tag class. So either assign it a value before rendering the view or use an integer type:
[MetadataType(typeof(TagValidation))]
public partial class Tag
{
public int TagId { get; set; }
public string Name { get; set; }
}
so that the generated hidden field looks like this:
<input
data-val="true"
data-val-number="The field TagId must be a number."
data-val-required="The TagId field is required."
id="TagId"
name="TagId"
type="hidden"
value="0"
/>
Also normally client side validation shouldn't be triggered for this hidden field.

jquery validate target cheking "disabled" html attribute.
$(function () {
$("#TagId").attr("disabled", "disabled")
});
or use Nullable.
hope this code!

With MVC4 you can also use this:
#{ Html.EnableClientValidation(false); }
#Html.EditorForModel()
#{ Html.EnableClientValidation(true); }

Make your Model or View-Model property value-types "nullabel". This will solve your problem.One important thing that remove "required" attribute from your tag otherwise it will take i "required"
Example:-
public class ViewModle
{
public int? foo{get;set;}
}
Here in example foo is integer nullable type, this will no longer required in mvc.
Hope this will help you.

I had the unfortunate experience that my model attributes were suddenly required causing my web APIs to return 400 errors when attributes were missing from web requests.
I found out this was caused by an accidental change of the "Nullable" setting in the project (Project properties -> Build -> Nullable: Enable"
After changing Nullable to Disable, all was good again.
I found a more detailed explanation of the issue here:
https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references#nullable-contexts

Related

ASP.NET MVC form without helper

I would like to create a form in my asp.net MVC website, without using HTML helpers like #Html.EditorFor()...
I also want to use Jquery Mobile.
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset data-role="controlgroup" data-iconpos="right">
<legend>Languages :</legend>
<input type="checkbox" name="checkbox-h-6a" id="checkbox-h-6a">
<label for="checkbox-h-6a"></label>
<input type="checkbox" name="checkbox-h-6b" id="checkbox-h-6b">
<label for="checkbox-h-6b">French</label>
<input type="checkbox" name="checkbox-h-6c" id="checkbox-h-6c">
<label for="checkbox-h-6c">German</label>
</fieldset>
}
I would like to use it with my model. (for ex. "bool UseEnglish", "bool UseFrench",..)
How can I do it simply ?
The only thing the helpers really do is abstract the name attributes of the form fields away. If you don't want to use the helpers, you will just have to make sure the name attributes are set to the right thing so that they will match up to your model after POST.
For simple properties, the name is just the property name, so given:
public string Foo { get; set; }
You'd have an input like:
<input type="text" name="Foo">
For things like complex types or reference navigation properties, you'll just chain the property names together until you're at the level you need, so given:
public Foo Foo { get; set; }
And:
public class Foo
{
public string Bar { get; set; }
}
You'll end up with:
<input type="text" name="Foo.Bar">
Finally, for list-style properties, you'll just add an index, so given:
public List<string> Foos { get; set; }
Then:
<input type="text" name="Foos[0]">
<input type="text" name="Foos[1]">
<input type="text" name="Foos[2]">
And of course, you can put all of these principles together to model any relationship:
public List<Foo> Foos { get; set; }
Then:
<input type="text" name="Foos[0].Bar">

Boolean model binding issue in Mvc 4

I want to bind a boolean property to a hidden input controller, but the output html code was error
code as follows:
public class TestModel
{
public bool IsOk { get; set; }
public bool IsSuccess { get; set; }
}
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new TestModel { IsOk = false, IsSuccess = true });
}
}
<h2>Index</h2>
<p>#Model.IsOk</p>
<p>
<input type="hidden" value="#Model.IsOk" />
</p>
<p>
<input type="hidden" value="#Model.IsSuccess" />
</p>
Html Output
<h2>Index</h2>
<p>False</p> //works
<p>
<input type="hidden" /> //where is value?
</p>
<p>
<input type="hidden" value="value" /> //wath's this?
</p>
But if i use ToString(), all above works well, so is it my mistake?
In HTML when you have an attribute which functions as an on/off or true/false switch you remove the attribute when the attribute is off/false and add the attribute with the same value as the attribute name when the attribute is on/true. Razor provides you with that functionality as you have already experienced.
Perhaps you intend to use Html.HiddenFor in the view?
<p>
#Html.HiddenFor(m => m.IsOk)
</p>
<p>
#Html.HiddenFor(m => m.IsSuccess)
</p>
This will produce this HTML where you have value="False" and value="True" as you expect:
<p>
<input data-val="true" data-val-required="The IsOk field is required."
id="IsOk" name="IsOk" type="hidden" value="False" />
</p>
<p>
<input data-val="true" data-val-required="The IsSuccess field is required."
id="IsSuccess" name="IsSuccess" type="hidden" value="True" />
</p>
Also, the model binder will be able to round-trip you view model properties.
Html attributes requires string objects
It's not automatically converted
So you have to use ToString()
Please try this.
$('#controlId').is(":checked");

bool property without Required attribute is required

have a simple ViewModel with three properties like so:
public bool RememberMe { get; set; }
In my view I have a simple #Html.CheckBoxFor(p => p.RememberMe)
I am using Client Side validation enabled using Html.EnableClientValidation();
Why is this being set as a required field?
Try a nullable bool.
public bool? RememberMe { get; set; }
With reference types there are a number of default validation rules applied. If a reference type is not nullable, it becomes required by default. The best illustration of this is if you use a textbox to display some properties (not something you would do in your site, but good for testing purposes):
Model:
public bool? MyBool { get; set; }
public int MyInt { get; set; }
View:
#Html.TextBoxFor(p => p.MyBool)
#Html.TextBoxFor(p => p.MyInt)
You can see from a view source what happens on the page:
<input id="MyNullBool" name="MyNullBool" type="text" value="">
<input data-val="true" data-val-required="The MyBool field is required." id="MyBool" name="MyBool" type="text" value="False">
<input data-val="true" data-val-number="The field MyInt must be a number." data-val-required="The MyInt field is required." id="MyInt" name="MyInt" type="text" value="0">
The nullable bool has no validation attributes, whereas the bool has a data-val-required tag. The int has a data-val-required tag and a data-val-number attribute
Of course, on a checkbox this is all pretty redundant as it can only be checked (true) or not checked (false) so a required tag isn't much use.
#Html.CheckBoxFor(c => c.TermsAndConditions, new { required = "required" })
#Html.ValidationMessageFor(c => c.TermsAndConditions, "you must agree to terms and conditions of Service.)"

How to create an EditorFor a collection and avoid doing a loop

For example:
Model
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Editor Template for Person (PersonEditor.cshtml):
#model MvcApplication1.Models.Person
#Html.HiddenFor(x=>x.ID)
<label>First Name</label>
#Html.TextBoxFor(x=>x.FirstName)
<label>Last Name</label>
#Html.TextBoxFor(x=>x.LastName)
<br />
On my main page, I want to be able to do the following:
#model IList<MvcApplication1.Models.Person>
#using (Html.BeginForm())
{
#Html.EditorFor(x=>x,"PersonEditor")
}
And have all the elements in the form, generate the proper names automatically; instead of having to loop through the collection as I am doing now:
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Count; i++)
{
#Html.EditorFor(x=>Model[i],"PersonEditor")
}
}
The form elements must contain the following format:
<input name="[0].ID" type="text" value="Some ID" />
<input name="[0].FirstName" type="text" value="Some value" />
<input name="[1].ID" type="text" value="Some x" />
<input name="[1].FirstName" type="text" value="Some y" />
And so on...
Because in my controller, I expect to receive an IList<Person> when the form posts pack.
Can I eliminate that for loop completely?
EDIT
Right now, when I simply do #Html.EditorFor(x=>x) (without the loop, in other words), I get this exception:
The model item passed into the dictionary is of type
'MvcApplication1.Models.Person[]', but this dictionary requires a
model item of type 'MvcApplication1.Models.Person'.
You should be able to use the same template for both IEnumerable<T> and T. The Templating is smart enough to enumerate the IEnumerable, but you will need to rename the editor template to match the type name. Then you should be able to use
#model IList<MvcApplication1.Models.Person>
#using (Html.BeginForm())
{
#Html.EditorForModel()
}
Unfortunately, it looks like a template named anything other than the type name will throw an exception
The model item passed into the dictionary is of type
'MvcApplication1.Models.Person[]', but this dictionary requires a
model item of type 'MvcApplication1.Models.Person'

Class Libraries, MVC, and Data Annotation

I'm new to MVC and am unsure about proper design.
I have class objects which I use in a variety of applications. I have taken the approach to write a custom view model class so that I can have access to properties in all of these objects and have strong typing. Without re-typing all my class code in the view model is there any way to have the properties in these objects validated using data annotation? Please let me know if my approach and design is all wrong.
[Required]
public User user = new User("username");
//User has lots properites and methods, could i validate inside my class code?
//What I'd like to avoid is putting the following stuff in my custom view model class, //since I already have a class library with this stuff:
public class User
{
[Required]
[StringLength(160)]
public string prop1 { get; set; }
[Required]
[StringLength(160)]
public string prop2 { get; set; }
[Required]
[StringLength(160)]
public string prop3 { get; set; }
public User(string token)
{
SetUser(token);
}
public void SetUser(string token)
{
this.prop1 = "this";
this.prop2 = "this2";
this.prop3 = "this3";
}
============
Good to know I can, but I'm stumbling on some issues. In my view I have: #Html.EditorFor(modelItem => modelItem.user.prop1)
I put the data annotation stuff in my class domain. When it renders it does show the annoations.
<input class="text-box single-line" data-val="true" data-val-length="The field prop1 must be a string with a maximum length of 5." data-val-length-max="5" data-val-required="The prop1 field is required." id="user_prop1" name="user.prop1" type="text" value="somevalue" />
but when I go to my controller the parameter is null. I think because the name is user.prop1. I tried a textbox where i specified the name attribute and yet my controller still couldn't get a value for my parameter.
====================
#model TrainingCalendar.Models.Training
#{
ViewBag.Title = "Signup";
}
<h2>Signup</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("ConfirmSignup", "Training", FormMethod.Post))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Training</legend>
<p>
#Html.Label("date", Model.SpecifiedCourse.strClassDate)
</p>
<p>
#Html.Label("time", Model.SpecifiedCourse.Time)
</p>
<p>
#Html.Label("instructor", Model.SpecifiedCourse.Instructor)
</p>
<p>
#Html.Hidden("id", Model.SpecifiedCourse.ID)
</p>
<table>
<tr>
<td>#Html.LabelFor(modelItem => modelItem.ApplicationUser.prop1)</td>
<td>#Html.EditorFor(modelItem => modelItem.ApplicationUser.prop1)</td>
<td style="color:Red">#Html.ValidationMessageFor(modelItem => modelItem.ApplicationUser.prop1)</td>
</tr>
<tr>
<td>#Html.LabelFor(modelItem => modelItem.ApplicationUser.prop2)</td>
<td>#Html.EditorFor(modelItem => modelItem.ApplicationUser.prop2)</td>
<td style="color:Red">#Html.ValidationMessageFor(modelItem => modelItem.ApplicationUser.prop2)</td>
</tr>
</table>
<p>
<input type="submit" value="Sign Up" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
===================
public ActionResult ConfirmSignup(
int id,
string prop1,
string prop2)
{
SignUpForClass();
return View();
}
Absolutely you can have the data annotation attributes on your class code. If you're encapsulating the class in your view model, populated the properties of the encapsulated class in your view. During the validation process the class members will be validated against the data annotation attributes that you specify in the class declaration.
I stumbled along this too and what I ended up doing (I don't know if it is the best or most correct) is too have data annotations that effect primarily the database in the DOM of my class library, such as MaxLength, Required, etc, and then in my view models I have the data annotations that relate primarily to validation such as Regular Expression, Date Time Formats, Max Values, or Max Lengths. In this way I keep the the roles of the two different aspects of the system separate. The View Model is a translation from my DOM to a format that my View can work with and my DOM is specifically for defining what the data should look like in the database.

Resources