Atrybut MaxLength nie generuje atrybutów walidacji po stronie klienta

83

Mam ciekawy problem z walidacją po stronie klienta ASP.NET MVC3. Mam następującą klasę:

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; }
}

Z mojego punktu widzenia:

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

A oto wygenerowany kod HTML, który otrzymałem dla pola tekstowego dla tego pola:

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

Brak śladu MaxLengthAttribute, ale wszystko inne wydaje się działać.

Jakieś pomysły, co się dzieje?

Matt Jenkins
źródło

Odpowiedzi:

151

Spróbuj użyć [StringLength]atrybutu:

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

To do celów walidacji. Jeśli chcesz ustawić na przykład atrybut maxlength na wejściu, możesz napisać niestandardowego dostawcę metadanych adnotacji danych, jak pokazano w tym poście, i dostosować domyślne szablony .

Darin Dimitrov
źródło
3
Dziękuję Ci bardzo. Na wypadek, gdyby ktoś się zastanawiał, sprawdziłem, że EF Code First tworzy kolumnę DB o maksymalnej długości narzuconej przez atrybut StringLength. :)
Matt Jenkins
Ale to nie jest odpowiedź. Ponieważ atrybut MinLength też nie działa
Hryhorii
@Greg, atrybut StringLength może określać maksymalną i minimalną długość.
Caleb Vear
Co jeśli nie chcesz określać maksymalnej długości, tylko minutę? Zasugerowałeś najbardziej funkcjonalne obejście, ale nie rozwiązuje ono problemu, że atrybut walidacji nie jest poprawnie sprawdzany.
Jeremy Holovacs,
7
@JeremyHolovacs, jeśli nie chcesz określać int.MaxValuemaksymalnej długości , po prostu określ jako maksymalną długość, a następnie ustaw właściwość MinLength na dowolną minimalną długość, której potrzebujesz. Więc nie, to nie jest tylko funkcjonalne obejście. To działa również w praktyce.
Darin Dimitrov
36

Właśnie użyłem fragmentu jQuery, aby rozwiązać ten problem.

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

Selektor znajduje wszystkie elementy, które mają ustawiony atrybut data-val-length-max. Jest to atrybut, który zostanie ustawiony przez atrybut walidacji StringLength.

Każda pętla przechodzi przez te dopasowania i analizuje wartość tego atrybutu i przypisuje ją do właściwości mxlength, która powinna zostać ustawiona.

Po prostu dodaj to do swojej funkcji przygotowania dokumentu i gotowe.

Nick Harrison
źródło
To jest całkiem sprytne.
TheOptimusPrimus
4
możesz użyć .data ("val-length-max") zamiast .attr ("data-val-length-max") :)
hatsrumandcode
8

MaxLengthAttributedziała od aktualizacji MVC 5.1: zmiany notatek

Michael Logutov
źródło
14
Teraz działa do walidacji, ale MVC nadal nie stosuje maxlengthatrybutu do inputelementu.
ladenedge
1
Napisałem rozszerzenie frameworka , które dokładnie to robi @ladenedge
Christian Gollhardt
8

W MVC 4 Jeśli chcesz mieć maksymalną długość w tekście wejściowym? Możesz !

@Html.TextBoxFor(model => model.Item3.ADR_ZIP, new { @class = "gui-input ui-oblig", @maxlength = "5" })
Saint-martin Guillaume
źródło
4

Podziękowania dla @ Nicka-Harrisona za jego odpowiedź:

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

Zastanawiałem się, do czego służy parseInt ()? Uprościłem to do tego bez żadnych problemów ...

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

Skomentowałbym odpowiedź Nicka, ale nie mam jeszcze wystarczającej liczby powtórzeń.

EamonnM
źródło
3

Miałem ten sam problem i udało mi się go rozwiązać, implementując interfejs IValidatableObject w moim modelu widoku.

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;
    }
}

Znacznik w Razor jest więc ...

<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>

To działa naprawdę dobrze. Jeśli zamiast tego spróbuję użyć [StringLength], renderowany kod HTML jest po prostu nieprawidłowy. Walidacja powinna wyglądać następująco:

<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>

W przypadku StringLengthAttribute renderowany kod HTML jest wyświetlany jako podsumowanie walidacji, które nie jest poprawne. Zabawne jest to, że jeśli walidator się nie powiedzie, przesyłanie jest nadal blokowane!

ScottyMacDev
źródło
2

StringLength działa świetnie, użyłem go w ten sposób:

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

lub po prostu użyjRegularExpression bez StringLength:

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

ale dla mnie powyższe metody dały błąd w widoku wyświetlacza, ponieważ miałem już pole ProductName w bazie danych, które miało więcej niż 25 znaków

więc w końcu natknąłem się na ten i ten post i próbowałem zweryfikować bez modelu w ten sposób :

 <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>

rozwiązało to mój problem, możesz również przeprowadzić walidację ręcznie za pomocą jquery lub za pomocą ModelState.AddModelError

nadzieja pomaga komuś.

Shaiju T
źródło
2

Wiem, że jestem bardzo spóźniony na przyjęcie, ale w końcu dowiedziałem się, jak możemy zarejestrować MaxLengthAttribute.

Najpierw potrzebujemy walidatora:

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;
    }
}

Nic specjalnego. W konstruktorze zapisujemy pewne wartości z atrybutu. W GetClientValidationRulesmamy ustaloną regułę. ValidationType = "length"jest odwzorowywany data-val-lengthprzez framework. rule.ValidationParameters["max"]dotyczy data-val-length-maxatrybutu.

Teraz, skoro masz walidator, musisz go zarejestrować tylko w global.asax:

protected void Application_Start()
{
    //...

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

Et voila, po prostu działa.

Christian Gollhardt
źródło
1

Próbowałem tego dla wszystkich danych wejściowych w moim dokumencie html (obszar tekstowy, dane wejściowe itp.), Które miały właściwość data-val-length-max i działa poprawnie.

$(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);
    });
});
Mario Durán Alvarez
źródło
0

Może to zastąpić MaxLength i MinLength

[StringLength(40, MinimumLength = 10 , ErrorMessage = "Name cannot be longer than 40 characters and less than 10")]
amal50
źródło
0
<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;
});
Nithya
źródło
6
Dodaj opis
King Stone