So ändern Sie die Nachrichtenüberprüfung 'data-val-number' in MVC, während sie vom @ HTML-Helfer generiert wird

76

Angenommen, dieses Modell:

Public Class Detail
    ...
    <DisplayName("Custom DisplayName")>
    <Required(ErrorMessage:="Custom ErrorMessage")>
    Public Property PercentChange As Integer
    ...
end class

und die Aussicht:

@Html.TextBoxFor(Function(m) m.PercentChange)

wird dieses HTML fortsetzen:

   <input data-val="true" 
    data-val-number="The field 'Custom DisplayName' must be a number." 
    data-val-required="Custom ErrorMessage"     
    id="PercentChange" 
    name="PercentChange" type="text" value="0" />

Ich möchte die data-val-numberFehlermeldung anpassen, die meiner Meinung nach generiert wurde, weil PercentChangees sich um eine handelt Integer. Ich habe nach einem solchen Attribut gesucht, um es zu ändern, rangeoder was auch immer damit zusammenhängt, funktioniert nicht.
Ich weiß, dass es eine Chance gibt, die unauffällige js-Datei selbst zu bearbeiten oder sie auf der Clientseite zu überschreiben. Ich möchte die data-val-numberFehlermeldung genau wie andere auf der Serverseite ändern .

GtEx
quelle
Ich habe Griffin MVC Contrib- Projekt verwendet, um die Validierungstexte ohne hässliche Attribute zu lokalisieren
Eduardo Molteni

Antworten:

39

Das wird nicht einfach. Die Standardnachricht wird als eingebettete Ressource in der System.Web.MvcAssembly gespeichert, und die abzurufende Methode ist eine private statische Methode einer internen versiegelten inneren Klasse ( System.Web.Mvc.ClientDataTypeModelValidatorProvider+NumericModelValidator.MakeErrorString). Es ist, als hätte der Typ bei Microsoft, der dies codiert, ein streng geheimes versteckt :-)

Sie können sich den folgenden Blog-Beitrag ansehen, der eine mögliche Lösung beschreibt. Grundsätzlich müssen Sie den vorhandenen ClientDataTypeModelValidatorProvider durch einen benutzerdefinierten ersetzen .

Wenn Ihnen die Hardcore-Codierung, die Sie ausführen müssen, nicht gefällt, können Sie diesen ganzzahligen Wert in Ihrem Ansichtsmodell auch durch eine Zeichenfolge ersetzen und ein benutzerdefiniertes Validierungsattribut verwenden, das die Analyse durchführt und eine benutzerdefinierte Fehlermeldung bereitstellt (welche) könnte sogar lokalisiert werden).

Darin Dimitrov
quelle
3
wow, du musst sehr gut in diesen geheimen Dingen sein :). sehr harter Code. Ich verstehe nicht, warum sie es so schwierig machen! Ich habe gehofft, dass sie diese Probleme in MVC3 RTM beheben, aber sie haben es nicht getan. Ich lege es in meinen Controller, um zu überprüfen, ob es funktioniert. und danke, es funktioniert wie ein Zauber. aber wo soll ich das removeding hinstellen? in meinem global.asax? ist es dann ok
GtEx
2
@GtEx, ja das Providers.Removeund Providers.Addsollte in die Application_StartMethode Ihres Global.asax eingefügt werden.
Darin Dimitrov
War jemand in der Lage, etwas Ähnliches zu tun, aber die Nachricht basierend auf anderen Werten aus dem Objekt zu erstellen, für das die Nummer validiert wird? Wenn mein Objekt beispielsweise "Eingabeaufforderung" und "Wert" hätte, würde meine Nachricht lauten: "{Eingabeaufforderung} muss eine Zahl sein." Oder muss ich dafür eine Objektvalidierung durchführen?
Richard B
8
Wenn wir uns den Quellcode von MVC 4 ResourceClassKey ansehen
MartinHN
3
Ja. In diesem Fall funktionierte das Ersetzen von ResourceClassKey und die Verwendung des Namens FieldMustBeNumeric. Verwenden Sie für erforderliche und ungültige Fehlermeldungen diesen anderen Ansatz, der auch von Darin Dimitrov vorgeschlagen wird. Stackoverflow.com/questions/12545176/…
Eduardo
77

Sie können die Nachricht überschreiben, indem Sie beim Rendern des Felds das Attribut data-val-number selbst angeben. Dies überschreibt die Standardnachricht. Dies funktioniert zumindest mit MVC 4.

@ Html.EditorFor (model => model.MyNumberField, new {data_val_number = "Geben Sie eine Ganzzahl an, Alter!"})

Denken Sie daran, dass Sie im Attributnamen einen Unterstrich verwenden müssen, damit Razor Ihr Attribut akzeptiert.

HenningJ
quelle
Ich weiß nicht, wie du es zum Laufen gebracht hast. In meiner MVC4-App wird das so hinzugefügte Attribut ignoriert.
Blazkovicz
Das hat bei mir funktioniert, obwohl ich es in meinem Fall nur ausschalten wollte. data_val = "false" genügte.
James
4
Ich kann nicht sehen, wie das funktionieren würde. Wenn dies ein neues htmlAttributesObjekt wäre, könnte es funktionieren, aber es ist ein additionalViewDataObjekt, sodass Sie nur ein zusätzliches Attribut namens erhalten data_val_number.
Sprintstar
3
@HenningJ Funktioniert hervorragend für mich, danke, dass Sie mir ein paar Zeit gespart haben :)
David Work
3
Ich stimme @Sprintstar zu. Es hat bei mir nicht funktioniert, aber es hat @Html.EditorFor(model => model.age, new { htmlAttributes = new { @class = "form-control", data_val_required = "Field required"} })funktioniert.
Benutzer1
52

Was Sie tun müssen, ist:

Fügen Sie den folgenden Code innerhalb Application_Start()in Global.asax:

 ClientDataTypeModelValidatorProvider.ResourceClassKey = "Messages";
 DefaultModelBinder.ResourceClassKey = "Messages";

Klicken Sie mit der rechten Maustaste auf Ihr ASP.NET MVC-Projekt in VS. Wählen Sie Add => Add ASP.NET Folder => App_GlobalResources.

Fügen Sie eine .resxDatei mit dem Namen hinzuMessages.resx in diesem Ordner .

Fügen Sie der .resxDatei folgende Zeichenfolgenressourcen hinzu :

FieldMustBeDate        The field {0} must be a date.
FieldMustBeNumeric     The field {0} must be a number.
PropertyValueInvalid   The value '{0}' is not valid for {1}.
PropertyValueRequired  A value is required.

Ändere den FieldMustBeNumericWert wie du willst ... :)

Du bist fertig.


Überprüfen Sie diesen Beitrag für weitere Details:

Lokalisieren von Standardfehlermeldungen in ASP.NET MVC und WebForms

Leniel Maccaferri
quelle
Ich erhalte keine Übersetzung für PropertyValueInvalid, nur den Standardtext ... Irgendwelche Ideen?
user1073075
4
Während @DarinDimitrov eine gültige Lösung hat, sollte dies für mich die akzeptierte Antwort sein, da es einen eingebauten erweiterbaren Mechanismus verwendet, anstatt die gesamte .Net-Klasse zu kopieren und zu ändern, was viel weniger wartbar ist
tsemer
5
Dies ist wirklich die beste Antwort auf diese Frage.
Nick Coad
2
@ Nick Coad. Ja, es ist das Beste - und keine einfache Antwort auf diese Frage. Ich kann bestätigen, dass es in MVC 4 und MVC 5
funktioniert
23

Als alternative Möglichkeit habe ich ein RegularExpression-Attribut angewendet, um den ungültigen Eintrag abzufangen und meine Nachricht dort festzulegen:

[RegularExpression(@"[0-9]*$", ErrorMessage = "Please enter a valid number ")]

Dies war ein kleiner Hack, aber dies schien der Komplexität der anderen Lösungen vorzuziehen, zumindest in meiner speziellen Situation.

EDIT: Dies hat in MVC3 gut funktioniert, aber es scheint, dass es bessere Lösungen für MVC4 + gibt.

Matthew Nichols
quelle
1
Trotzdem bevorzuge ich die erste Lösung, viel ordentlicher. nur eine Codezeile in global.asax. Das Problem dabei ist, dass Sie dieses Attribut zu jeder einzelnen Say-Integer-Eigenschaft in Ihrem Projekt hinzufügen müssen und es einen regulären Ausdruck verwendet, was mich mit dem Prozess unwohl macht, der nur 0,0001 Sekunden benötigt, um es zu registrieren;)
GtEx
2
Kein Argument, dass es ein bisschen ein Hack ist. Meine einzige Verteidigung, dass es extrem einfach sein sollte, Fehlermeldungen im Framework zu überschreiben.
Matthew Nichols
Nun, der richtige Weg wäre, es auf einfache Weise überschreibbar zu machen ... aber da wir das nicht können, bin ich froh, dass Sie meine Gedanken nützlich fanden.
Matthew Nichols
Scheint wie der Dollar, den ich nicht brauchte, vielleicht sind ^ und $ dann implizit.
Anders Lindén
Ich kenne das OP nur als Ganzzahl, aber dies wird schnell außer Kontrolle geraten, wenn Sie Dezimalzahlen und i18n berücksichtigen.
Phuzi
11

Aus diesem Buch über MVC 3, das ich habe. Alles was Sie tun müssen ist Folgendes:

public class ClientNumberValidatorProvider : ClientDataTypeModelValidatorProvider 
{ 
   public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, 
                                                          ControllerContext context) 
   { 
       bool isNumericField = base.GetValidators(metadata, context).Any(); 
       if (isNumericField) 
           yield return new ClientSideNumberValidator(metadata, context); 
   } 
} 

public class ClientSideNumberValidator : ModelValidator 
{ 
  public ClientSideNumberValidator(ModelMetadata metadata,  
      ControllerContext controllerContext) : base(metadata, controllerContext) { } 

  public override IEnumerable<ModelValidationResult> Validate(object container) 
  { 
     yield break; // Do nothing for server-side validation 
  } 

  public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() 
  { 
     yield return new ModelClientValidationRule { 
        ValidationType = "number", 
        ErrorMessage = string.Format(CultureInfo.CurrentCulture,  
                                     ValidationMessages.MustBeNumber,  
                                     Metadata.GetDisplayName()) 
        }; 
  } 
} 

protected void Application_Start() 
{ 
    // Leave the rest of this method unchanged 

    var existingProvider = ModelValidatorProviders.Providers 
        .Single(x => x is ClientDataTypeModelValidatorProvider); 
    ModelValidatorProviders.Providers.Remove(existingProvider); 
    ModelValidatorProviders.Providers.Add(new ClientNumberValidatorProvider()); 
} 

Beachten Sie, wie die ErrorMessage ausgegeben wird, Sie geben die aktuelle Kultur an und die lokalisierte Nachricht wird aus der .resx-Ressourcendatei ValidationMessages (hier kulturspezifisch) extrahiert. Wenn Sie das nicht brauchen, ersetzen Sie es einfach durch Ihre eigene Nachricht.

Denis Valeev
quelle
Es klingt viel komplexer als meine gewählte Lösung. yield returnGanz zu schweigen davon, dass wir keine in vb.net haben, obwohl es eine Lösung gibt. Trotzdem bekomme ich die Vorteile dieser Lösung nicht.
GtEx
Diese Lösung gefällt mir sehr gut. Eine Frage: Wir ersetzen den bestehenden Anbieter durch einen neuen. Was wäre, wenn der bestehende Anbieter früher mehr als nur diese Validierung durchgeführt hätte? Ist garantiert, dass durch das Entfernen keine anderen Funktionen beeinträchtigt werden?
Alex
7

Hier ist eine andere Lösung, die die Nachrichtenclientseite ohne geänderte MVC3-Quelle ändert. Alle Details in diesem Blog-Beitrag:

https://greenicicle.wordpress.com/2011/02/28/fixing-non-localizable-validation-messages-with-javascript/

Kurz gesagt, müssen Sie nach dem Laden der jQuery-Validierung das folgende Skript sowie die entsprechende Lokalisierungsdatei einfügen .

(function ($) {
    // Walk through the adapters that connect unobstrusive validation to jQuery.validate.
    // Look for all adapters that perform number validation
    $.each($.validator.unobtrusive.adapters, function () {
        if (this.name === "number") {
            // Get the method called by the adapter, and replace it with one 
            // that changes the message to the jQuery.validate default message
            // that can be globalized. If that string contains a {0} placeholder, 
            // it is replaced by the field name.
            var baseAdapt = this.adapt;
            this.adapt = function (options) {
                var fieldName = new RegExp("The field (.+) must be a number").exec(options.message)[1];
                options.message = $.validator.format($.validator.messages.number, fieldName);
                baseAdapt(options);
            };
        }
    });
} (jQuery));
Phil Hale
quelle
Danke Phil, (+1 für) das ist gut zu wissen. Aber ich persönlich ändere MVC3 eher in diesem speziellen Problem. Ich hoffe, sie haben dieses Update in der nächsten Version veröffentlicht.
GtEx
4

Sie können ResourceKey der ClientDataTypeModelValidatorProvider-Klasse auf den Namen einer globalen Ressource setzen, die den FieldMustBeNumeric-Schlüssel enthält, um die Fehlermeldung mvc-Validierung der Nummer durch Ihre benutzerdefinierte Nachricht zu ersetzen. Eine weitere Fehlermeldung zur Überprüfung des Datums ist FieldMustBeDate.

ClientDataTypeModelValidatorProvider.ResourceKey="MyResources"; // MyResource is my global resource
reza taroosheh
quelle
1
Erstaunlich, das funktioniert tatsächlich! Es ist ResourceClassKeyaber nichtResourceKey
Vincent Sels
3

Hier ist eine andere Lösung in reinem js, die funktioniert, wenn Sie Nachrichten global und nicht benutzerdefinierte Nachrichten für jedes Element angeben möchten.

Der Schlüssel ist, dass Validierungsnachrichten jquery.validation.unobtrusive.jsmithilfe des data-val-xxxAttributs für jedes Element festgelegt werden. Alles, was Sie tun müssen, ist, diese Nachrichten zu ersetzen, bevor die Bibliothek sie verwendet. Es ist ein bisschen schmutzig, aber ich wollte nur die Arbeit erledigen und schnell erledigen. Hier geht es also um die Validierung des Nummerntyps:

    $('[data-val-number]').each(function () {
    var el = $(this);
    var orig = el.data('val-number');

    var fieldName = orig.replace('The field ', '');
    fieldName = fieldName.replace(' must be a number.', '');

    el.attr('data-val-number', fieldName + ' باید عددی باشد')
});

Das Gute ist, dass es nicht kompiliert werden muss und Sie es später problemlos erweitern können, obwohl es nicht robust, aber schnell ist.

Mohas
quelle
2

Überprüfen Sie dies auch:

Das vollständige Handbuch zur Validierung in ASP.NET MVC 3 - Teil 2

Hauptteile des Artikels folgen (kopiert).

Das Erstellen eines voll funktionsfähigen benutzerdefinierten Validators, der sowohl auf dem Client als auch auf dem Server funktioniert, besteht aus vier Teilen. Zuerst unterklassifizieren wir ValidationAttributeunsere serverseitige Validierungslogik und fügen sie hinzu. Als nächstes implementieren wir IClientValidatableunser Attribut, damit HTML5- data-*Attribute an den Client übergeben werden können. Drittens schreiben wir eine benutzerdefinierte JavaScript-Funktion, die die Validierung auf dem Client durchführt. Schließlich erstellen wir einen Adapter, um die HTML5-Attribute in ein Format umzuwandeln, das unsere benutzerdefinierte Funktion verstehen kann. Das klingt nach viel Arbeit, aber sobald Sie anfangen, werden Sie es relativ einfach finden.

Unterklasse ValidationAttribute

In diesem Beispiel schreiben wir einen NotEqualTo-Validator, der einfach überprüft, ob der Wert einer Eigenschaft nicht dem Wert einer anderen entspricht.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class NotEqualToAttribute : ValidationAttribute
{
    private const string DefaultErrorMessage = "{0} cannot be the same as {1}.";

    public string OtherProperty { get; private set; }

    public NotEqualToAttribute(string otherProperty)
        : base(DefaultErrorMessage)
    {
        if (string.IsNullOrEmpty(otherProperty))
        {
            throw new ArgumentNullException("otherProperty");
        }

        OtherProperty = otherProperty;
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format(ErrorMessageString, name, OtherProperty);
    }

    protected override ValidationResult IsValid(object value, 
        ValidationContext validationContext)
    {
        if (value != null)
        {
            var otherProperty = validationContext.ObjectInstance.GetType()
                .GetProperty(OtherProperty);

            var otherPropertyValue = otherProperty
                .GetValue(validationContext.ObjectInstance, null);

            if (value.Equals(otherPropertyValue))
            {
                return new ValidationResult(
                    FormatErrorMessage(validationContext.DisplayName));
            }
        }
    return ValidationResult.Success;
    }        
}

Fügen Sie das neue Attribut der Kennworteigenschaft des RegisterModels hinzu und führen Sie die Anwendung aus.

[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
[NotEqualTo("UserName")]
public string Password { get; set; }
...

Implementierung von IClientValidatable

ASP.NET MVC 2 hatte einen Mechanismus zum Hinzufügen einer clientseitigen Validierung, der jedoch nicht sehr hübsch war. Zum Glück haben sich die Dinge in MVC 3 verbessert und der Prozess ist jetzt ziemlich trivial und beinhaltet zum Glück keine Änderung des Global.asaxwie in der vorherigen Version.

Der erste Schritt besteht darin, dass Ihr benutzerdefiniertes Validierungsattribut IClientValidatable implementiert. Dies ist eine einfache Schnittstelle mit einer Methode:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
    ModelMetadata metadata,
    ControllerContext context)
{
    var clientValidationRule = new ModelClientValidationRule()
    {
        ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
        ValidationType = "notequalto"
    };

    clientValidationRule.ValidationParameters.Add("otherproperty", OtherProperty);

    return new[] { clientValidationRule };
}

Wenn Sie die Anwendung jetzt ausführen und die Quelle anzeigen, sehen Sie, dass der HTML-Code für die Passworteingabe jetzt Ihre Datenattribute enthält notequalto:

<div class="editor-field">
    <input data-val="true" data-val-notequalto="Password cannot be the same as UserName." 
    data-val-notequalto-otherproperty="UserName" 
    data-val-regex="Weak password detected." 
    data-val-regex-pattern="^(?!password$)(?!12345$).*" 
    data-val-required="The Password field is required." 
    id="Password" name="Password" type="password" />
    <span class="hint">Enter your password here</span>
    <span class="field-validation-valid" data-valmsg-for="Password" 
    data-valmsg-replace="true"></span>
</div>

Erstellen einer benutzerdefinierten jQuery-Validierungsfunktion

Der gesamte Code sollte am besten in einer separaten JavaScript-Datei abgelegt werden.

(function ($) {
    $.validator.addMethod("notequalto", function (value, element, params) {
        if (!this.optional(element)) {
            var otherProp = $('#' + params);
            return (otherProp.val() != 
        }
    return true;
});

$.validator.unobtrusive.adapters.addSingleVal("notequalto", "otherproperty");

}(jQuery));

Abhängig von Ihren Validierungsanforderungen stellen Sie möglicherweise fest, dass die Bibliothek jquery.validate bereits den Code enthält, den Sie für die Validierung selbst benötigen. In jquery.validate gibt es viele Validatoren, die nicht implementiert oder Datenanmerkungen zugeordnet wurden. Wenn diese also Ihren Anforderungen entsprechen, müssen Sie nur einen Adapter oder sogar einen Aufruf eines integrierten Adapters schreiben, der dies kann sei so klein wie eine einzelne Zeile. Schauen Sie sich jquery.validate.js an nach, um herauszufinden, was verfügbar ist.

Verwenden eines vorhandenen Adapters jquery.validate.unobtrusive

Die Aufgabe des Adapters besteht darin, die HTML5- data-*Attribute Ihres Formularelements zu lesen und diese Daten in ein Formular zu konvertieren, das von jquery.validate und Ihrer benutzerdefinierten Validierungsfunktion verstanden werden kann. Sie müssen jedoch nicht die ganze Arbeit selbst erledigen, und in vielen Fällen können Sie einen eingebauten Adapter aufrufen. jquery.validate.unobtrusive deklariert drei integrierte Adapter, die in den meisten Situationen verwendet werden können. Diese sind:

jQuery.validator.unobtrusive.adapters.addBool - used when your validator does not need any additional data.
jQuery.validator.unobtrusive.adapters.addSingleVal - used when your validator takes in one piece of additional data.
jQuery.validator.unobtrusive.adapters.addMinMax - used when your validator deals with minimum and maximum values such as range or string length.

Wenn Ihr Validator nicht in eine dieser Kategorien passt, müssen Sie mit der jQuery.validator.unobtrusive.adapters.addMethode Ihren eigenen Adapter schreiben . Dies ist nicht so schwierig, wie es sich anhört, und wir werden später in diesem Artikel ein Beispiel sehen.

Wir verwenden die addSingleValMethode, indem wir den Namen des Adapters und den Namen des einzelnen Werts übergeben, den wir übergeben möchten. Sollte sich der Name der Validierungsfunktion vom Adapter unterscheiden, können Sie einen dritten Parameter ( ruleName) übergeben:

jQuery.validator.unobtrusive.adapters.addSingleVal("notequalto", "otherproperty", "mynotequaltofunction");

Zu diesem Zeitpunkt ist unser benutzerdefinierter Validator abgeschlossen.

Zum besseren Verständnis lesen Sie den Artikel selbst, der eine ausführlichere Beschreibung und ein komplexeres Beispiel enthält.

HTH.

Kamran
quelle
1

Ich habe das gerade gemacht und dann einen regulären Ausdruck verwendet:

$(document).ready(function () {
    $.validator.methods.number = function (e) {
        return true;
    };
});


[RegularExpression(@"^[0-9\.]*$", ErrorMessage = "Invalid Amount")]
public decimal? Amount { get; set; }
rauben
quelle
1

Oder Sie können dies einfach tun.

@Html.ValidationMessageFor(m => m.PercentChange, "Custom Message: Input value must be a number"), new { @style = "display:none" })

Hoffe das hilft.

Edward Disi
quelle
1

Ich mache das, indem ich das auf meine Sicht stelle

@Html.DropDownListFor(m => m.BenefNamePos, Model.Options, new { onchange = "changePosition(this);", @class="form-control", data_val_number = "This is my custom message" })
Fernando Campos
quelle
0

Ich habe dieses Problem in KendoGrid. Ich verwende ein Skript am Ende der Ansicht, um die Datenwertnummer zu überschreiben:

@(Html.Kendo().Grid<Test.ViewModel>(Model)
  .Name("listado")
  ...
  .Columns(columns =>
    {
        columns.Bound("idElementColumn").Filterable(false);
    ...
    }

Und zumindest habe ich am Ende von View Folgendes eingefügt:

<script type="text/javascript">
        $("#listado").on("click", function (e) {
            $(".k-grid #idElementColumn").attr('data-val-number', 'Ingrese un número.');
        });    
</script>
Hernaldo Gonzalez
quelle
0

Eine einfache Methode ist die Verwendung der Meldung zur Änderung der Datenanmerkung in ViewModel:

[Required(ErrorMessage ="الزامی")]
[StringLength(maximumLength:50,MinimumLength =2)]
[Display(Name = "نام")]
public string FirstName { get; set; }
Omid Marofpor
quelle