data-val-date attribute added to dateTimePicker even without ValidationAttribute in model - asp.net-mvc

I emit a datetimepicker with this line
#Html.EditorFor(model => model.Event.EventStart)
My editor template is like this:
#model DateTime?
#Html.TextBox("", string.Format("{0:yyyy-MM-dd HH:mm}",
Model.HasValue ? Model : null), new { #class = "dateTimePicker" })
and that will result in the following html:
<input type="text" value="2013-10-26 00:00" name="Event.EventStart"
id="Event_EventStart" data-val-required="The Startdatum field is required."
data-val-date="The field Startdatum must be a date." data-val="true"
class="dateTimePicker hasDatepicker input-validation-error">
That is also the case if I do not have a #Html.ValidationMessageFor(model => model.Event.EventStart) line.
There is nothing in my code that should enable validation for the property EventStart.
In my model it only looks like this:
[Display(Name = "Startdatum")]
public DateTime EventStart { get; set; }
It seems like jquery ui (most suspect) is adding validation for dates to my dateTimePicker.
Why?
Or is the presence of the data-val-date attribute also triggering datetime validation?
The validation fails:
The field Startdatum must be a date.
It would be nice to either be able to disable the validation or add validation for datetimes instead of dates.
Is there some way to actually have the responsibility to add validation myself with data annotations as I do usually (with other datatypes)?

Related

Build list of data validation attributes for a given element

When using any of the Input Extension Helper Methods, like #Html.TextboxFor, any Validation Attributes from your model are automatically generated by the Razor engine (via ClientValidationEnabled/UnobtrusiveJavaScriptEnabled).
For example, take the following case which works fine
Model:
[Required]
public string QuestionOne { get; set; }
View:
#Html.TextBoxFor(model => model.QuestionOne)
#Html.ValidationMessageFor(model => model.QuestionOne)
Generated Markup:
<input type="text" id="QuestionOne" name="QuestionOne" value=""
data-val="true" data-val-required="The QuestionOne field is required." >
<span class="field-validation-valid" data-valmsg-for="QuestionOne" data-valmsg-replace="true"></span>
In this case the attributes data-val="true" & data-val-required="The QuestionOne field is required." are picked up by Unobtrusive validation and the form element is successfully validated.
However, for extensibility reasons, I want to be able to generate the <input> element myself instead of using TextBoxFor. So my view would now look like this:
<input type="textbox"
id="#Html.IdFor(m => m.QuestionTwo)"
name="#Html.NameFor(m => m.QuestionTwo)"
value="#Model.QuestionTwo"
data-val="true" data-val-required="Selection is Required" />
#Html.ValidationMessageFor(model => model.QuestionTwo)
In this case, I'm faking the validation attribute output by just re-writing data-val="true" (etc) by hand, but this would have to be expanded to cover every single case.
Here's a running Demo in .NET Fiddle
Q: Can I build /return a list of data-val-* attributes for a given element?
You can use the GetUnobtrusiveValidationAttributes() method of HtmlHelper to get the validation attributes associated with a specific property.
For example in the view
#{ var attributes = Html.GetUnobtrusiveValidationAttributes("QuestionTwo"); }
<input
type="textbox"
#foreach(var attr in attributes)
{
#:#attr.Key="#attr.Value"
}
id="#Html.IdFor(m => m.QuestionTwo)"
....
/>
Note the #:#attr.Key="#attr.Value" line will give a warning (Missing attribute name) but will run correctly
Alternatively, you could use javaScript/jQuery to add the attributes
<script type="text/javascript">
var attributes = #Html.Raw(Json.Encode(attributes));
var input = $('#QuestionTwo');
for(var i in attributes) {
input.attr(i, attributes[i]);
}
</script>
I have forked the DotNetFiddle here to show the working code for both options.
While the above code shows how it can be done, you should not be doing that. The HtmlHelper methods execute a lot of code your ignoring to ensure correct 2-way model binding, for example, the value attribute is determined by first checking for a value in ModelState, then in the ViewDataDictionary, and only if the previous values do not exist, does it use the value of the property (the second part of TextBoxFor displaying initial value, not the value updated from code explains the behavior).
Except for the incorrect value attribute, the code you have shown for the <input> is the same as will be generated by simply using #Html.TextBoxFor(m => m.Question2). I assume your real case is different, but if you cannot make use of TextBoxFor() and using an overload that accepts htmlAttributes to generate the html you need, then the correct approach is to create your own HtmlHelper method (and making use of existing methods in the HtmlHelper class and System.Web.Mvc.Html namespace)

How can I remove the "type" attribute from Html.EditorFor?

In my View, I have a Html.EditorFor line:
#Html.EditorFor(model => model.DateCreditEarned,
new { htmlAttributes = new { #class = "form-control" } })
When I look at the rendered HTML, I see that the input has a "type = date" attribute. Is there any way I can use EditorFor and remove the type attribute? This way I can have the desired functionality without causing Edge problems.
<input
class="form-control datecontrol"
data-val="true"
data-val-date="The field Date Credit Earned must be a date."
data-val-required="The Date Credit Earned field is required."
id="DateCreditEarned"
name="DateCreditEarned"
type="date" value="6/27/2016">
Update
I discovered that I can use Html.TextboxFor without having any Edge problems (i.e., Edge overriding my Bootstrap datepicker with its own). But I was hoping I could get the same thing done with EditorFor.
Well, making my comment an answer, in ASP.NET MVC You typically do things declaratively in models. In this case a text field was wanted so making the model property like this:
[DataType(DataType.Text)]
public DateTime DateCreditEarned { get; set; }
solved the problem.

MVC Validation model attributes: Client side validation not occurring, only server side

I have jQuery.Validation 1.15.0 installed in an ASP.NET MVC 5.2 project and I am trying to learn the basics of MVC and, at the moment, validating model data.
I was under the impression that the Validation Attributes that I add would generate client side validations and that a post back would not occur until all client validations were passed. However, though the validations are generally working, a post back occurs every time, even when, for example, a value for VendorId is not provided.
public class StateAssessmentViewModel
{
[Required(ErrorMessage = "Vendor ID is required")]
[MaxLength(4, ErrorMessage="{0} can not be longer than {1} characters." )]
[Display(Name = "Vendor ID")]
public int VendorId { get; set; }
[Display(Name = "Effective Date")]
[DisplayFormat(DataFormatString = "{0:MM-dd-yyyy}", ApplyFormatInEditMode = true)]
public DateTime EffectiveDate { get; set; }
}
This site confirms that the validations should be first performed on the client if Javascript were enabled in the browser, Chrome v49, and it is. In fact, the "Effective Date" field has a working jQuery calendar on it, so Javascript is definitely enabled.
What can I look for to help me identify why the validations are not being performed at all on the client?
Here is the produced html when I view source:
<div class="form-group">
<label class="control-label col-md-2" for="VendorId">Vendor ID</label>
<div class="col-md-10">
<input class="input-validation-error form-control text-box single-line" data-val="true" data-val-maxlength="Vendor ID can not be longer than 4 characters." data-val-maxlength-max="4" data-val-number="The field Vendor ID must be a number." data-val-required="Vendor ID is required" id="VendorId" max="4" min="0" name="VendorId" type="number" value="3333a" />
<span class="field-validation-error text-danger" data-valmsg-for="VendorId" data-valmsg-replace="true">The value '3333a' is not valid for Vendor ID.</span>
</div>
</div>
I'm using Bootstrap, too, and trying to filter out non numeric keystrokes for the VendorId field, but this has no effect. I also tried playing around setting max and min lengths.
I also was expecting the DisplayFormat attribute on the EffectiveDate model property to have some client side effect. It did not. Here's the generated HTML from doing a the "View Source"
<div class="form-group">
<label class="control-label col-md-2" for="EffectiveDate">Effective Date</label>
<div class="col-md-10">
<input class="input-validation-error form-control text-box single-line" data-val="true" data-val-date="The field Effective Date must be a date." data-val-required="The Effective Date field is required." id="txtEffectiveDate" name="EffectiveDate" type="datetime" value="" />
<span class="field-validation-error text-danger" data-valmsg-for="EffectiveDate" data-valmsg-replace="true">The Effective Date field is required.</span>
</div>
</div>
Why is client side validation not occurring?
Are you including the jquery validation library in your BungleConfig.cs class ? If so, are you including that script bundle in your layout file? It should be working given the HTML that it's producing

Why validation is not working with Html.TextBoxFor but is when using Html.EditorFor?

I am trying MVC's datatype attributes, and just created a simple scenario like the following:
The View:
#model MVC4.Models.Model
#{
ViewBag.Title = "DataTypeAttribute";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<h2>DataTypeAttribute</h2>
#using (Html.BeginForm("SubmitData", "Home"))
{
<div>
#Html.ValidationSummary()
</div>
#Html.EditorFor(m => m.Email)
<br />
<br />
#Html.EditorFor(m => m.PostalCode)
<br />
<br />
#Html.EditorFor(m => m.TextOnly)
<br />
<br />
<button type="submit">Submit</button>
}
The Model:
public class Model
{
[DataType(DataType.EmailAddress)]
[Required]
//[EmailAddress]
public string Email { get; set; }
[DataType(DataType.PostalCode)]
public string PostalCode { get; set; }
public string TextOnly { get; set; }
}
"SubmitData" is just a controller that, returns View(..., model) if ModelState.IsValid is false.
Although posts like this do a good job in tackling the differences between Html.TextBoxFor and Html.EditorFor, I could not find an answer as to why validation for the datatype EmailAddress will not work when using TextBoxFor. I did find people mentioning TextBoxFor does not take metadata into account, while EditorFor does.
But does this make sense ? So TextBoxFor does not offer support for client validations ?!
I wonder what is the reason for the difference between the two ?
TextBoxFor() does work with validations.
[DataType(DataType.EmailAddress)] is not a validation attribute. Its an attribute that determines the type of input to display by setting the type attribute in the rendered html. For example <input type="text" ..>, <input type="date" ..>, <input type="email" ..> in order to render the browsers implementation of a HTML4 datepicker, email input etc.. It works only for EditorFor() because TextBoxFor() as its name suggest generates and input with type="text"
If you want validation for an email address, then you use the [EmailAddress] attribute on your property.
[Required]
[EmailAddress]
public string Email { get; set; }
Edit (further to the comments)
One of the features of HTML5 is the ability to validate user data without relying on scripts. One such form of browser validation is using the type attribute. The use of [DataType(DataType.EmailAddress)] on a property that is rendered with #Html.EditorFor() adds type="email" to the input element. From the MDN documentation
email: The element represents one email address. Line breaks are automatically stripped from the input value. An invalid email address can be set, but the input field will only satisfy its constraints if the email address satisfies the ABNF production 1*( atext / "." ) "#" ldh-str 1*( "." ldh-str ) where atext is defined in RFC 5322 section 3.2.3, and ldh-str is defined in RFC 1034 section 3.5.
If your are currently seeing a validation error message associated with the property, and you have not added the [EmailAddress] attribute, then it means that jquery.validate.js is not loaded and you are seeing the browsers error message associated with type="email".
When jquery.validate.js is loaded (correctly), the novalidate="novalidate" attribute is added to the form element, which specifies that form is not to be validated (using the HTML5 validation) when submitted. The relevant code from jquery.validate.js is (approx line 35)
// Add novalidate tag if HTML5.
this.attr('novalidate', 'novalidate');
This is added to prevent possible confusion between error messages displayed by browser validation and jquery unobtrusive validation.
As to why DataTypeAttribute attribute inherits ValidationAttribute when it does not actually do validation, from Brad Wilson himself in this answer
The reason it derives from ValidationAttribute is so that you could create a new custom data type class, which was both a DataType and a Validation, all wrapped up into one. It's an unfortunate side-effect of .NET not allowing multiple inheritance.

#Html.EditorFor How to make attribute type="email"

I can do this easily using a TextBoxFor but how do I do it with an EditorFor?
I figured using the DataAnnotation [DataType(DataType.EmailAddress)] but that doesn't do it.
I don't quite understand what the DataType annotation actually does because it doesn't seem to do anything at all at first glance.
You can override the HTML Attributes, to which a browser will fallback to type='text' if they do not support it:
#Html.TextBoxFor(m => m.Email, new { #type = "email" })
it seems to be supported now.
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control", #type = "email" } })
The EditorFor helper method is somewhat limited out of the box and doesn't yet appear to support the HTML5 type="email" attribute.
Your options right now seem to be either using TextBoxFor or creating a custom template that will allow you to set the input's type attribute. Here's another thread that reviews some of the options for creating your own templates.
The DataAnnotation [DataType(DataType.EmailAddress)] is actually quite useful. It sets the id and name of your form field to email, which you can use with jQuery validation to show the user client-side validation messages. Applying the DataAnnotation to your model class also means that the email property on your model will be automatically validated on the server side. If you enable unobtrusive validation in your app, you get client- and servers-side validation almost for free.
As an addition to jortizromo's answer, you have now at least two options:
Specifying #type in the htmlAttributes parameter for method EditorFor() as in
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #type = "email" } })
Using the EmailAddress annotation attribute from System.ComponentModel.DataAnnotations namespace in the model class definition for the corresponding Email property and a simple call to method EditorFor() (this provides HTML validation data tags which could be a good or bad idea depending on your task) as in
ViewModel
[EmailAddress]
public string Email { get; set; }
Razor View
#Html.EditorFor(model => model.Email)

Resources