MaxLength Attribute not generating client-side validation attributes
Asked Answered
E

11

88

I have a curious problem with ASP.NET MVC3 client-side validation. I have the following class:

public class Instrument : BaseObject
{
    public int Id { get; set; }

    [Required(ErrorMessage = "Name is required.")]
    [MaxLength(40, ErrorMessage = "Name cannot be longer than 40 characters.")]
    public string Name { get; set; }
}

From my view:

<div class="editor-field">
    @Html.EditorFor(model => model.Name)
    @Html.ValidationMessageFor(model => model.Name)
</div>

And here's the generated HTML I get for the textbox for this field:

<input class="text-box single-line" data-val="true" data-val-required="Name is required." id="Name" name="Name" type="text" value="">

No sign of the MaxLengthAttribute, but everything else seems to be working.

Any ideas what's going wrong?

Exceeding answered 23/7, 2011 at 16:10 Comment(0)
B
159

Try using the [StringLength] attribute:

[Required(ErrorMessage = "Name is required.")]
[StringLength(40, ErrorMessage = "Name cannot be longer than 40 characters.")]
public string Name { get; set; }

That's for validation purposes. If you want to set for example the maxlength attribute on the input you could write a custom data annotations metadata provider as shown in this post and customize the default templates.

Bulkhead answered 23/7, 2011 at 19:23 Comment(10)
Thank you very much. In case anyone is wondering, I checked that EF Code First creates the DB column with the maximum length imposed by the StringLength attribute. :)Exceeding
But this is not the answer. Because MinLength attribute too not workPsf
@Greg, the StringLength attribute can specify a maximum and minimum length.Angellaangelle
What if you don't want to specify a max length, just a min? You have suggested a mostly functional workaround, but it does not address the issue that the validation attribute is not validating properly.Crew
@JeremyHolovacs, if you don't want to specify a max you simply specify int.MaxValue as max length and then set the MinLength property to whatever minimum length you need. So, no, it's not just a functional workaround. It's one that works in practice as well.Bulkhead
@DarinDimitrov if you specify int.MaxValue you are specifying a max length, because you have to. While functionally identical, you are now explicitly specifying something that you wouldn't have to with MinLength and MaxLength. This still does not address the issue that the validation attribute is not validating properly, and most people find this out the hard way.Crew
@JeremyHolovacs, personally I don't use data annotations to perform validation. I find them very limiting and counter intuitive. I prefer imperative validation rather than declarative. It gives me more control. I use FluentValidation.NET.Bulkhead
The problem with this answer is that MaxLength DOES correctly work server side, so saying MaxLength isn't for validation purposes seems odd.Prichard
@Prichard - MaxLength is for validation purposes - just not for client-side validation purposes.Gules
@MikeCole I believe the RangeAttribute in the same namespace does correctly cause client side validation to be emitted. Its supposed to be one of the selling points of Asp.Net MVC; it will pick up validation attributes and apply client side logic for the built-in ones.Prichard
S
37

I just used a snippet of jquery to solve this problem.

$("input[data-val-length-max]").each(function (index, element) {
   var length = parseInt($(this).attr("data-val-length-max"));
   $(this).prop("maxlength", length);
});

The selector finds all of the elements that have a data-val-length-max attribute set. This is the attribute that the StringLength validation attribute will set.

The each loop loops through these matches and will parse out the value for this attribute and assign it to the mxlength property that should have been set.

Just add this to you document ready function and you are good to go.

Subcontraoctave answered 4/1, 2013 at 22:34 Comment(2)
This is pretty slick.Depression
you can use .data("val-length-max") instead of .attr("data-val-length-max") :)Crotch
S
8

MaxLengthAttribute is working since MVC 5.1 update: change notes

Suchta answered 11/2, 2014 at 8:24 Comment(2)
It does now work for validation, but MVC still is not applying the maxlength attribute to the input element.Zeitler
I wrote an framework extension, that exactly does this @ZeitlerRabia
H
8

In MVC 4 If you want maxlenght in input type text ? You can !

@Html.TextBoxFor(model => model.Item3.ADR_ZIP, new { @class = "gui-input ui-oblig", @maxlength = "5" })
Haroldharolda answered 20/5, 2016 at 23:18 Comment(0)
D
4

Props to @Nick-Harrison for his answer:

$("input[data-val-length-max]").each(function (index, element) {
var length = parseInt($(this).attr("data-val-length-max"));
$(this).prop("maxlength", length);
});

I was wondering what the parseInt() is for there? I've simplified it to this with no problems...

$("input[data-val-length-max]").each(function (index, element) {
    element.setAttribute("maxlength", element.getAttribute("data-val-length-max"))
});

I would have commented on Nicks answer but don't have enough rep yet.

Dammar answered 27/7, 2016 at 10:35 Comment(0)
C
3

I had this same problem and I was able to solve it by implementing the IValidatableObject interface in my view model.

public class RegisterViewModel : IValidatableObject
{
    /// <summary>
    /// Error message for Minimum password
    /// </summary>
    public static string PasswordLengthErrorMessage => $"The password must be at least {PasswordMinimumLength} characters";

    /// <summary>
    /// Minimum acceptable password length
    /// </summary>
    public const int PasswordMinimumLength = 8;

    /// <summary>
    /// Gets or sets the password provided by the user.
    /// </summary>
    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    /// <summary>
    /// Only need to validate the minimum length
    /// </summary>
    /// <param name="validationContext">ValidationContext, ignored</param>
    /// <returns>List of validation errors</returns>
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var errorList = new List<ValidationResult>();
        if ((Password?.Length ?? 0 ) < PasswordMinimumLength)
        {
            errorList.Add(new ValidationResult(PasswordLengthErrorMessage, new List<string>() {"Password"}));
        }
        return errorList;
    }
}

The markup in the Razor is then...

<div class="form-group">
    @Html.LabelFor(m => m.Password)
    @Html.PasswordFor(m => m.Password, new { @class = "form-control input-lg" }
    <div class="password-helper">Must contain: 8 characters, 1 upper-case, 1 lower-case
    </div>
    @Html.ValidationMessagesFor(m => m.Password, new { @class = "text-danger" })
</div>

This works really well. If I attempt to use [StringLength] instead then the rendered HTML is just not correct. The validation should render as:

<span class="text-danger field-validation-invalid field-validation-error" data-valmsg-for="Password" data-valmsg-replace="true"><span id="Password-error" class="">The Password should be a minimum of 8 characters long.</span></span>

With the StringLengthAttribute the rendered HTML shows as a ValidationSummary which is not correct. The funny thing is that when the validator fails the submit is still blocked!

Coelom answered 21/4, 2016 at 22:24 Comment(0)
R
3

I know I am very late to the party, but I finaly found out how we can register the MaxLengthAttribute.

First we need a validator:

public class MaxLengthClientValidator : DataAnnotationsModelValidator<MaxLengthAttribute>
{
    private readonly string _errorMessage;
    private readonly int _length;


    public MaxLengthClientValidator(ModelMetadata metadata, ControllerContext context, MaxLengthAttribute attribute)
    : base(metadata, context, attribute)
    {
        _errorMessage = attribute.FormatErrorMessage(metadata.DisplayName);
        _length = attribute.Length;
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = _errorMessage,
            ValidationType = "length"
        };

        rule.ValidationParameters["max"] = _length;
        yield return rule;
    }
}

Nothing realy special. In the constructor we save some values from the attribute. In the GetClientValidationRules we set a rule. ValidationType = "length" is mapped to data-val-length by the framework. rule.ValidationParameters["max"] is for the data-val-length-max attribute.

Now since you have a validator, you only need to register it in global.asax:

protected void Application_Start()
{
    //...

    //Register Validator
    DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MaxLengthAttribute), typeof(MaxLengthClientValidator));
}

Et voila, it just works.

Rabia answered 21/4, 2017 at 17:45 Comment(0)
M
2

StringLength works great, i used it this way:

[StringLength(25,MinimumLength=1,ErrorMessage="Sorry only 25 characters allowed for 
              ProductName")]
public string ProductName { get; set; }

or Just Use RegularExpression without StringLength:

[RegularExpression(@"^[a-zA-Z0-9'@&#.\s]{1,25}$", ErrorMessage = "Reg Says Sorry only 25 
                   characters allowed for ProductName")]    
public string ProductName { get; set; }

but for me above methods gave error in display view, cause i had already ProductName field in database which had more than 25 characters

so finally i came across this and this post and tried to validate without model like this:

 <div class="editor-field">
 @Html.TextBoxFor(model => model.ProductName, new
 {
 @class = "form-control",
 data_val = "true",
 data_val_length = "Sorry only 25 characters allowed for ProductName",
 data_val_length_max = "25",
 data_val_length_min = "1"
 })
 <span class="validation"> @Html.ValidationMessageFor(model => model.ProductName)</span>
 </div>

this solved my issue, you can also do validation manually using jquery or using ModelState.AddModelError

hope helps someone.

Mangum answered 3/5, 2015 at 11:58 Comment(0)
E
1

I tried this for all the inputs in my html document(textarea,inputs,etc) that had the data-val-length-max property and it works correctly.

$(document).ready(function () {
    $(":input[data-val-length-max]").each(function (index, element) {
        var length = parseInt($(this).attr("data-val-length-max"));
        $(this).prop("maxlength", length);
    });
});
Eisenberg answered 16/10, 2017 at 18:13 Comment(0)
B
0

This can replace the MaxLength and the MinLength

[StringLength(40, MinimumLength = 10 , ErrorMessage = "Name cannot be longer than 40 characters and less than 10")]
Benevolent answered 19/11, 2016 at 3:31 Comment(0)
D
-1
<input class="text-box single-line" data-val="true" data-val-required="Name is required." 
    id="Name1" name="Name" type="text" value="">

$('#Name1').keypress(function () {
    if (this.value.length >= 5) return false;
});
Decrescendo answered 27/6, 2019 at 18:8 Comment(1)
Please add descriptionGentleman

© 2022 - 2024 — McMap. All rights reserved.