Model MVC wymaga true

85

Czy istnieje sposób, aby przez adnotacje danych wymagać, aby właściwość logiczna była ustawiona na wartość true?

public class MyAwesomeObj{
    public bool ThisMustBeTrue{get;set;}
}
Marty Trenouth
źródło
Jaki dokładnie jest przypadek użycia tego? Nie możesz po prostu pozwolić, aby właściwość była tylko do odczytu i przez cały czas zwracała wartość true?
Jan Thomä
1
To całkiem dużo do powiedzenia ... hej kolego, zapomniałeś sprawdzić Zgadzam się ... co powinno unieważnić model.
Marty Trenouth,
Myślę, że jest to coś, czym chciałbyś się zająć po stronie klienta.
PsychoCoder,
15
@PsychoCoder: Powinien być obsługiwany po obu stronach ... nie tylko po stronie klienta. Chciałem tylko sprawdzić, czy można to zrobić, dodając prostą adnotację danych.
Marty Trenouth,

Odpowiedzi:

49

Możesz stworzyć swój własny walidator:

public class IsTrueAttribute : ValidationAttribute
{
    #region Overrides of ValidationAttribute

    /// <summary>
    /// Determines whether the specified value of the object is valid. 
    /// </summary>
    /// <returns>
    /// true if the specified value is valid; otherwise, false. 
    /// </returns>
    /// <param name="value">The value of the specified validation object on which the <see cref="T:System.ComponentModel.DataAnnotations.ValidationAttribute"/> is declared.
    ///                 </param>
    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");

        return (bool) value;
    }

    #endregion
}
moribvndvs
źródło
Rozważę ulepszenie tego za pomocą implementacji po stronie klienta - zamiast korzystać ze zdalnej weryfikacji, o której mowa w innych odpowiedziach, użyj dyskretnej pisowni tutaj: jacopretorius.net/2011/01/client-side-validation-in-mvc-3 .html
SamStephens
To dla nas dobre (i sprawdzone) szybkie rozwiązanie. Możemy obejść się bez walidacji po stronie klienta w rozwiązaniu @ dazbradbury (również dobrym), ponieważ potrzebujemy tego tylko w pojedynczym polu wyboru na poprzedniej stronie ankiety.
Seth
return (bool) value == true;to jest zbędne porównanie
T-moty
130

Utworzyłbym walidator po stronie serwera i klienta. Korzystając z MVC i dyskretnego sprawdzania poprawności formularzy, można to osiągnąć, wykonując następujące czynności:

Najpierw utwórz klasę w swoim projekcie, aby przeprowadzić walidację po stronie serwera w następujący sposób:

public class EnforceTrueAttribute : ValidationAttribute, IClientValidatable
{
    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");
        return (bool)value == true;
    }

    public override string FormatErrorMessage(string name)
    {
        return "The " + name + " field must be checked in order to continue.";
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new ModelClientValidationRule
        {
            ErrorMessage = String.IsNullOrEmpty(ErrorMessage) ? FormatErrorMessage(metadata.DisplayName) : ErrorMessage,
            ValidationType = "enforcetrue"
        };
    }
}

Następnie opisz odpowiednią właściwość w modelu:

[EnforceTrue(ErrorMessage=@"Error Message")]
public bool ThisMustBeTrue{ get; set; }

Na koniec włącz walidację po stronie klienta, dodając następujący skrypt do widoku:

<script type="text/javascript">
    jQuery.validator.addMethod("enforcetrue", function (value, element, param) {
        return element.checked;
    });
    jQuery.validator.unobtrusive.adapters.addBool("enforcetrue");
</script>

Uwaga: stworzyliśmy już metodę, GetClientValidationRulesktóra wypycha naszą adnotację do widoku z naszego modelu.

Jeśli używasz plików zasobów do dostarczania komunikatu o błędzie do internacjonalizacji, usuń FormatErrorMessagepołączenie (lub po prostu zadzwoń do bazy) i dostosuj GetClientValidationRulesmetodę w następujący sposób:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    string errorMessage = String.Empty;
    if(String.IsNullOrWhiteSpace(ErrorMessage))
    {
        // Check if they supplied an error message resource
        if(ErrorMessageResourceType != null && !String.IsNullOrWhiteSpace(ErrorMessageResourceName))
        {
            var resMan = new ResourceManager(ErrorMessageResourceType.FullName, ErrorMessageResourceType.Assembly);
            errorMessage = resMan.GetString(ErrorMessageResourceName);
        }
    }
    else
    {
        errorMessage = ErrorMessage;
    }

    yield return new ModelClientValidationRule
    {
        ErrorMessage = errorMessage,
        ValidationType = "enforcetrue"
    };
}
dazbradbury
źródło
3
Dzięki za to - działa świetnie! Działa lepiej z usuniętą metodą override FormatErrorMessage - w ten sposób działa lokalizacja komunikatów o błędach z plików zasobów. Moje zastosowanie: [EnforceTrue (ErrorMessageResourceType = typeof (ValidationMessages), ErrorMessageResourceName = "TermsAndConditionsRequired")]
Matt Frear
2
Nie mogę uruchomić walidacji po stronie klienta i nie mogę powiedzieć, co robię źle. Gdzie dokładnie powinienem umieścić javacsript? W tagu głowy? Obok kontrolera?
vsdev,
Zgadzam się, to powinna być odpowiedź
Simua
1
Świetne rozwiązanie pokazujące moc niestandardowych atrybutów walidacji! Chociaż zalecam umieszczenie skryptu w pliku js, do którego istnieją odniesienia globalne, a nie widok (y), do ponownego wykorzystania. Najlepiej jest również obsłużyć wszystkie sposoby dodawania ciągów komunikatów: domyślny, jeśli nie podano, lub ciąg wiadomości lub z pliku zasobów.
jeepwran
1
Świetne rozwiązanie, dzięki za wysłanie. Dla tych, którzy nie mogą uruchomić walidacji po stronie klienta: Musisz rozszerzyć walidację jQuery przed załadowaniem kontrolek, które będzie sprawdzać, więc umieść skrypt w nagłówku, a nie w oknie .onload / $ (dokument ) .ready () zdarzenie.
Evert
92

Wiem, że to starszy post, ale chciałem udostępnić prosty sposób na wykonanie tego po stronie serwera. Tworzysz właściwość publiczną ustawioną na true i porównujesz swoją wartość bool z tą właściwością. Jeśli wartość bool nie jest zaznaczona (domyślnie false), formularz nie zostanie zweryfikowany.

public bool isTrue
{ get { return true; } }

[Required]
[Display(Name = "I agree to the terms and conditions")]
[Compare("isTrue", ErrorMessage = "Please agree to Terms and Conditions")]
public bool AgreeTerms { get; set; }

Kod Razor

@Html.CheckBoxFor(m => Model.AgreeTerms, new { id = "AgreeTerms", @checked = "checked" })
<label asp-for="AgreeTerms" class="control-label"></label>
<a target="_blank" href="/Terms">Read</a>
<br />
@Html.ValidationMessageFor(model => model.AgreeTerms, "", new { @class = "text-danger" })
@Html.HiddenFor(x => Model.isTrue)
fields.cage
źródło
12
+1 za prostotę. FYI: Musiałem upublicznić właściwość „isTrue”, aby to zadziałało.
Tod Birdsall
Porównania nie ma dla mnie w MVC4
Michael Rudner Evanchik
Super rozwiązanie, świetne rozwiązanie
Sreerejith SS
9
A jeśli dodasz ukrytą właściwość „isTrue”, otrzymasz walidację po stronie klienta
billoreid,
2
Irytujące to świetnie wyglądające rozwiązanie nie zadziałało dla mnie. Testowany na Mvc 5.2.3.
harvzor
22

Wypróbowałem kilka rozwiązań, ale żadne z nich nie działało całkowicie dla mnie, aby uzyskać weryfikację zarówno po stronie klienta, jak i serwera. Co więc zrobiłem w mojej aplikacji MVC 5, aby to zadziałało:

W Twoim ViewModel (do weryfikacji po stronie serwera):

public bool IsTrue => true;

[Required]
[Display(Name = "I agree to the terms and conditions")]
[Compare(nameof(IsTrue), ErrorMessage = "Please agree to Terms and Conditions")]
public bool HasAcceptedTermsAndConditions { get; set; }

Na stronie Razor (do weryfikacji po stronie klienta):

<div class="form-group">
   @Html.CheckBoxFor(m => m.HasAcceptedTermsAndConditions)
   @Html.LabelFor(m => m.HasAcceptedTermsAndConditions)
   @Html.ValidationMessageFor(m => m.HasAcceptedTermsAndConditions)

   @Html.Hidden(nameof(Model.IsTrue), "true")
</div>
Kapé
źródło
1
Urocze rozwiązanie!
Tobias,
3
Zwróć uwagę na wartość ukrytego pola („prawda”)!
Tobias,
10

Chciałbym tylko skierować ludzi do następującego Fiddle: https://dotnetfiddle.net/JbPh0X

Użytkownik dodał [Range(typeof(bool), "true", "true", ErrorMessage = "You gotta tick the box!")]do swojej właściwości logicznej, co powoduje, że walidacja po stronie serwera działa.

Aby działała również walidacja po stronie klienta, dodali następujący skrypt:

// extend jquery range validator to work for required checkboxes
var defaultRangeValidator = $.validator.methods.range;
$.validator.methods.range = function(value, element, param) {
    if(element.type === 'checkbox') {
        // if it's a checkbox return true if it is checked
        return element.checked;
    } else {
        // otherwise run the default validation function
        return defaultRangeValidator.call(this, value, element, param);
    }
}
harvzor
źródło
9

Po prostu sprawdź, czy jego reprezentacja w postaci ciągu jest równa True:

[RegularExpression("True")]
public bool TermsAndConditions { get; set; }
ta.speot.is
źródło
@JeradRose Jest sprawdzony poprawnie na serwerze. Czy masz na myśli walidację po stronie klienta?
ta.speot.is
3
Potwierdzone, działa to po stronie serwera, ale nie po stronie klienta
Matt Frear
Pomyślałem, że walidacja po stronie serwera może mieć wyjątek niezgodności typu, próbując porównać bool z ciągiem.
Jerad Rose
RegularExpressionAttributewewnętrznie używa Convert.ToStringdo pobrania ciągu reprezentującego wartość właściwości (która jest dostarczana do niego jako object).
ta.speot.is
Myślę, że ta odpowiedź jest prostsza niż @ fields-cage +1 ode mnie
Aaron Hudon
5

Możesz utworzyć własny atrybut lub użyć atrybutu CustomValidationAttribute .

Oto jak można użyć atrybutu CustomValidationAttribute:

[CustomValidation(typeof(BoolValidation), "ValidateBool")]

gdzie BoolValidation jest zdefiniowany jako:

public class BoolValidation
{
  public static ValidationResult ValidateBool(bool boolToBeTrue)
  {
    if (boolToBeTrue)
    {
      return ValidationResult.Success;
    }
    else
    {
      return new ValidationResult(
          "Bool must be true.");
    }
  }
Matthew Manela
źródło
5

[Required]atrybut oznacza wymaganie dowolnej wartości - może to być prawda lub fałsz. W tym celu musiałbyś użyć innej walidacji.

Sergey Kudriavtsev
źródło
3

W związku z postem ta.speot.is i komentarzem Jerada Rose'a:

Podany post nie będzie działał po stronie klienta z dyskretną weryfikacją. Powinno to działać w obu obozach (klient i serwer):

[RegularExpression("(True|true)")]
public bool TermsAndConditions { get; set; }
lobotomia
źródło
Nie wiem, czy jest to problem z nowszą wersją, ale nie działa dla mnie z jquery.validate 1.19.2 i jquery.validate.unobtrusive 3.2.11. Wydaje się, że problem polega na tym, że regexmetoda dyskretna definiuje najpierw sprawdzenie, czy pole wyboru jest opcjonalne przed walidacją wyrażenia regularnego, co ma sens, z wyjątkiem tego, że jquery.validate wydaje się traktować dowolne niezaznaczone pole wyboru jako opcjonalne. tl; dr Uruchamia wyrażenie regularne tylko na zaznaczonych polach wyboru. Możemy dodać podkładkę do regex validatormetody lub po prostu stworzyć własny walidator.
xr280xr
3

.NET Core MVC - wymagane pole wyboru z adnotacjami danych

public class MyModel
{
    [Display(Name = "Confirmation")]
    [Range(typeof(bool), "true", "true", ErrorMessage = "Please check the Confirmation checkbox.")]
    public bool IsConfirmed { get; set; }   
}

<div class="custom-control custom-checkbox col-10">
    <input type="checkbox" asp-for="IsConfirmed" class="custom-control-input" />
    <label class="custom-control-label" for="IsConfirmed">
        "By clicking 'submit', I confirm."
    </label>
    <span asp-validation-for="IsConfirmed" class="text-danger"></span>
</div>

<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>

<script type="text/javascript">
    $(document).ready(function () {
        // extend range validator method to treat checkboxes differently
        var defaultRangeValidator = $.validator.methods.range;
        $.validator.methods.range = function (value, element, param) {
            if (element.type === 'checkbox') {
                // if it's a checkbox return true if it is checked
                return element.checked;
            } else {
                // otherwise run the default validation function
                return defaultRangeValidator.call(this, value, element, param);
            }
        }
    });
</script>
Aung San Myint
źródło
2

Nie znam sposobu na adnotacje danych, ale można to łatwo zrobić w kontrolerze.

public ActionResult Add(Domain.Something model)
{

    if (!model.MyCheckBox)
        ModelState.AddModelError("MyCheckBox", "You forgot to click accept");

    if (ModelState.IsValid) {
        //'# do your stuff
    }

}

Jedyną inną opcją byłoby zbudowanie niestandardowego walidatora po stronie serwera i zdalnego walidatora po stronie klienta (zdalna walidacja jest dostępna tylko w MVC3 +)

Ścigaj Florell
źródło
Trochę już nowe, jak już sprawdzić flagę boolowską ... chciałem wiedzieć, czy istnieje dla niej adnotacja danych.
Marty Trenouth
2

Czy masz odpowiednie elementy w pliku web.config ?

Może to spowodować, że walidacja nie zadziała.

Możesz także spróbować utworzyć niestandardowy atrybut walidacji (ponieważ [Required]dba tylko o to, czy istnieje, a Ty dbasz o wartość):

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class RequiredTrueAttribute : ValidationAttribute
{
    // Internal field to hold the mask value.
    readonly bool accepted;

    public bool Accepted
    {
        get { return accepted; }
    }

    public RequiredTrueAttribute(bool accepted)
    {
        this.accepted = accepted;
    }

    public override bool IsValid(object value)
    {
        bool isAccepted = (bool)value;
        return (isAccepted == true);
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentCulture,
          ErrorMessageString, name, this.Accepted);
    }
}

Następnie użycie:

[RequiredTrue(ErrorMessage="{0} requires acceptance to continue.")]
public bool Agreement {get; set;}

Od tutaj .

George Stocker
źródło
2

To właśnie zadziałało dla mnie. Nic innego nie zrobiło. Mvc 5:

Model

public string True
{
  get
  {
    return "true";
  }
}

[Required]
[Compare("True", ErrorMessage = "Please agree to the Acknowlegement")]
public bool Acknowlegement { get; set; }

Widok

  @Html.HiddenFor(m => m.True)
  @Html.EditorFor(model => model.Acknowlegement, new { htmlAttributes = Model.Attributes })
  @Html.ValidationMessageFor(model => model.Acknowlegement, "", new { @class = "text-danger" })

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

toddmo
źródło
2

W przypadku ASP.NET Core MVC jest tutaj weryfikacja klienta i serwera, oparta na rozwiązaniu firmy Dazbradbury

public class EnforceTrueAttribute : ValidationAttribute, IClientModelValidator
{
    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");
        return (bool)value;
    }

    public void AddValidation(ClientModelValidationContext context)
    {
        MergeAttribute(context.Attributes, "data-val", "true");
        var errorMessage = ErrorMessage ?? 
            $"The value for field {context.ModelMetadata.GetDisplayName()} must be true.";
        MergeAttribute(context.Attributes, "data-val-enforcetrue", errorMessage);
    }

    private void MergeAttribute(IDictionary<string, string> attributes,
        string key,
        string value)
    {
        if (attributes.ContainsKey(key))
        {
            return;
        }
        attributes.Add(key, value);
    }
}

A potem na kliencie:

$.validator.addMethod("enforcetrue", function (value, element, param) {
    return element.checked;
});

$.validator.unobtrusive.adapters.addBool("enforcetrue");

Wtedy użycie to:

[EnforceTrue(ErrorMessage = "Please tick the checkbox")]
public bool IsAccepted { get; set; }
Matt
źródło
1

Próbowałem użyć odpowiedzi fields.cage i nie do końca mi się to udało, ale zadziałało coś prostszego i nie wiem dokładnie dlaczego (może inna wersja Razor?), Ale wszystko, co musiałem zrobić, to:

[Required]
[Range(typeof(bool), "true", "true", ErrorMessage = "Agreement required.")]
[Display(Name = "By clicking here, I agree that my firstborn child will etc etc...")]
public bool Agreement1Checked { get; set; }

A w pliku .cshtml:

@Html.CheckBoxFor(m => m.Agreement1Checked)
@Html.LabelFor(m => m.Agreement1Checked)
@Html.ValidationMessageFor(m => m.Agreement1Checked)
Dronz
źródło
Dla mnie to nie działa po stronie klienta. Z jakiegoś powodu parametr przekazany do metody reguły jquery.validate jest [NaN, NaN]tam, gdzie powinien[true, true]
xr280xr
@ xr280xr Nawet jeśli użytkownik zaznaczy pole wyboru?
Dronz
0

Myślę, że najlepszym sposobem na rozwiązanie tego problemu jest po prostu sprawdzenie w kontrolerze, czy pole jest prawdziwe, w przeciwnym razie po prostu dodaj błąd do modelu i poproś o ponowne wyświetlenie widoku.

Jak już wspomniano, wszystko [Wymagane] to upewnienie się, że istnieje wartość, aw twoim przypadku, jeśli nie jest zaznaczone, nadal otrzymasz fałsz.

samack
źródło
0

Sprawdź tutaj niezawodną weryfikację . Możesz go pobrać / zainstalować za pośrednictwem Nuget.

To świetna mała biblioteka do tego typu rzeczy.

DavidWainwright
źródło
Ehhhh ... Domyślne atrybuty walidacji działają jednak całkiem dobrze.
Pangamma
0
/// <summary> 
///  Summary : -CheckBox for or input type check required validation is not working the root cause and solution as follows
///
///  Problem :
///  The key to this problem lies in interpretation of jQuery validation 'required' rule. I digged a little and find a specific code inside a jquery.validate.unobtrusive.js file:
///  adapters.add("required", function (options) {
///  if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") {
///    setValidationValues(options, "required", true);
///    }
///   });
///   
///  Fix: (Jquery script fix at page level added in to check box required area)
///  jQuery.validator.unobtrusive.adapters.add("brequired", function (options) {
///   if (options.element.tagName.toUpperCase() == "INPUT" && options.element.type.toUpperCase() == "CHECKBOX") {
///              options.rules["required"] = true;
///   if (options.message) {
///                   options.messages["required"] = options.message;
///                       }
///  Fix : (C# Code for MVC validation)
///  You can see it inherits from common RequiredAttribute. Moreover it implements IClientValidateable. This is to make assure that rule will be propagated to client side (jQuery validation) as well.
///  
///  Annotation example :
///   [BooleanRequired]
///   public bool iAgree { get; set' }
/// </summary>


public class BooleanRequired : RequiredAttribute, IClientValidatable
{

    public BooleanRequired()
    {
    }

    public override bool IsValid(object value)
    {
        return value != null && (bool)value == true;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        return new ModelClientValidationRule[] { new ModelClientValidationRule() { ValidationType = "brequired", ErrorMessage = this.ErrorMessage } };
    }
}
dhandapani harikrishnan
źródło
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
Ravi Dhoriya ツ
To działa Sprawdź ten link i podaj
itmeze.com/2010/12/06/...
Dziś to działa. Czy możesz być pewien, że będzie działać za 5, 10 lat później? Ta baza danych pytań i odpowiedzi jest również tworzona dla przyszłych użytkowników
Eliyahu