I have a problem with displaying validation message in a Blazor app.
I have the following model (for testing purposes):
public class MyModel
{
[Required(ErrorMessage = "Amount is required")]
public int Amount { get; set; }
[Required(ErrorMessage = "NullableAmount is required")]
public int? NullableAmount { get; set; }
[RequiredIf(nameof(Foo), new[] { "bar" }, ErrorMessage = "Id is required")]
public int Id { get; set; }
[RequiredIf(nameof(Foo), new[] { "bar" }, ErrorMessage = "NullableId is required")]
public int? NullableId { get; set; }
public string Foo { get; set; }
}
I decorated the properties with the built-in RequiredAttribute, and created a custom RequiredIfAttribute, here's the code for that:
public class RequiredIfAttribute : ValidationAttribute
{
private readonly string _otherPropertyName;
private readonly string[] _otherPropertyValues;
public RequiredIfAttribute(string otherPropertyName, string[] otherPropertyValues)
{
_otherPropertyName = otherPropertyName;
_otherPropertyValues = otherPropertyValues;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var instance = validationContext.ObjectInstance;
var type = instance.GetType();
var propertyValue = type.GetProperty(_otherPropertyName)?.GetValue(instance);
if (!_otherPropertyValues.Contains(propertyValue?.ToString()))
{
return ValidationResult.Success;
}
if(value == null)
{
return new ValidationResult(ErrorMessage);
}
if(int.TryParse(value.ToString(), out var intValue))
{
if(intValue == 0)
{
return new ValidationResult(ErrorMessage);
}
}
return ValidationResult.Success;
}
}
I takes the name of the other property, the values for that property, and if the value of that other property matches one of the specified values, it checks if the property decorated with RequiredIf is null, or if it is an integer, 0. (I use it for a model with a nullable int property, which should not be null or 0 if the other property has a certain value).
On the WebAPI side it works fine, but in the Blazor form I have some problems. Here's my form:
<EditForm Model="MyModel" OnValidSubmit="OnSubmit">
<div style="display: flex; flex-direction: column; width: 300px;">
<DataAnnotationsValidator />
<ValidationSummary />
<label>
Foo:
<InputText #bind-Value="MyModel.Foo" />
</label>
<ValidationMessage For="#(() => MyModel.Foo)" />
<label>
Amount:
<InputNumber #bind-Value="MyModel.Amount" />
</label>
<ValidationMessage For="#(() => MyModel.Amount)" />
<label>
Null Amount:
<InputNumber #bind-Value="MyModel.NullableAmount" />
</label>
<ValidationMessage For="#(() => MyModel.NullableAmount)" />
<label>
Id:
<InputNumber #bind-Value="MyModel.Id" />
</label>
<ValidationMessage For="#(() => MyModel.Id)" />
<label>
Null Id:
<InputNumber #bind-Value="MyModel.NullableId" />
</label>
<ValidationMessage For="#(() => MyModel.NullableId)" />
<button type="submit">submit</button>
</div>
</EditForm>
I open the form, enter the value "bar" for Foo, than click submit, I get the following result:
Amount is valid, as it has the default value 0, and RequiredAttribute only checks for nulls.
NullAmount is invalid, it is shown in the ValidationSummary, the input field is also red, as it is invalid, and the validation message is also shown.
Id is invalid, as in the RequiredIf attribute I check int value against 0, it has the default 0 value, the error message is shown in the ValidationSummary, and here comes the interesting part, the input field is not red, and the validation message is not shown.
The same applies for the Nullable Id field, the error message is shown in the validation summary, but the field is not red, and the validation message is not shown.
Now, if I enter a valid value for Id and NullableId (the fields with my custom attribute), and after that I change Id to 0, and NullableId to empty, this is what happens:
Both input fields change to red, validation message is shown for both properties, but in the validation summary, both error messages are displayed twice.
Now, if I click submit, both input fields change to green, and the validation messages are gone, but still displayed in the summary.
I'm totally lost, don't know if I have a problem with my custom validation attribute, a problem in the Blazor component, or it is an unknown issue. The built-in Required attribute works fine, I just added it to the code to see if my form works with that.
Do you have any idea?
I've finally found the problem. In the validation attribute, when returning an error result, I have to pass memberName in the constructor.
return new ValidationResult(ErrorMessage, validationContext.MemberName)
Thank you so much #petyusa, I also ran into this issue and it was driving me nuts. I stumbled across your solution and it solved it immediately. However, I had to pass an array of string to the second argument (perhaps because I'm using .NET 5.0) instead of a simple string:
return new ValidationResult(ErrorMessage, new []{validationContext.MemberName});
I'm trying to get Scott Kirkland's DataAnnotationsExtensions to work with my MVC4 project. But I'm having problems with the client side validation of an email address. I've added a EmailAddress annotation with a error message, but when I enter an invalid email address I do not get the custom error message, but instead I get the generic email error message "Please enter a valid RecipientEmail address.".
My class looks like this:
public class NpRequest
{
[DisplayName("Telefonnummer som skal overdrages")]
[Required(ErrorMessage = "Angiv telefonnummeret som skal overdrages")]
public string PhoneNumer { get; set; }
[DisplayName("Recipient email address")]
[EmailAddress(ErrorMessage = "This is my custom error message")]
[Required(ErrorMessage = "The recipient email address is required")]
public string RecipientEmail { get; set; }
public RecipientTypeEnum RecipientType { get; set; }
}
And my view:
---SNIPPET BEGIN---
<div class="editor-label">
#Html.LabelFor(model => model.PhoneNumer)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PhoneNumer)
#Html.ValidationMessageFor(model => model.PhoneNumer)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.RecipientEmail)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RecipientEmail)
#Html.ValidationMessageFor(model => model.RecipientEmail)
</div>
<p>
<input type="submit" value="Create" />
</p>
---SNIPPET END---
EDIT:
When I inspect the HTML it looks like this:
<input class="text-box single-line input-validation-error" data-val="true" data-val-email="This is my custom error message" data-val-required="The recipient email address is required" id="RecipientEmail" name="RecipientEmail" type="email" value="">
It seems that my custom error message is put into the data-val-email attribute. I was under the impression that the DataAnnotationExtension automatically added my custom error message to the ModelState and thereby also adding it to the field-validation-error span, which is showing the MVC validation error.
Is this assumption wrong? Should I write my own javascript, which extracts the custom error message attribute and injects it into the field-validation-error span?
Can anyone see what I'm doing wrong?
I ended up using a mix of System.ComponentModel.DataAnnotations and DataAnnotationsExtensions. I found out out that most of the time data annotations will also make client side validation. The only time where is no client side validation, is when I check if the phone number is the correct length.
public class NpRequest
{
[DisplayName("Phone number")]
[MinLengthAttribute(8, ErrorMessage = "Phone number must be 8 digits")]
[MaxLengthAttribute(8, ErrorMessage = "Phone number must be 8 digits")]
[DigitsAttribute(ErrorMessage = "Phone number must be 8 digits")]
[Required(ErrorMessage = "Phone number is required")]
public string PhoneNumber { get; set; }
[DisplayName("Modtagers email adresse")]
[EmailAddressAttribute(ErrorMessage = "Invalid email")]
[Required(ErrorMessage = "Email is required")]
public string RecipientEmail { get; set; }
public RecipientTypeEnum RecipientType { get; set; }
}
input class="text-box single-line" data-val="true" data-val-date="The field Bill Date must be a date." data-val-required="Bill Date is required" id="BillDate" name="BillDate" type="date" value="22.02.2012"
here is html code shown in source on chrome. But there is no value in the textbox.
And here is my model for the "BillDate"
[Required]
[Display(Name = "Bill Date")]
[DataType(DataType.Date, ErrorMessage = "Not a valid date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime BillDate { set; get; }
and this is how i am trying to display it #Html.EditorFor(model => model.BillDate)
Can you help me please...
I had this exact same problem. I ended up looking at the HTTP Post that was going back to the Controller and the format of the date in the POST was yyyy-MM-dd.
I changed my data annotation to this and it worked...
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
Hope that helps!
i got a template created for custom zip code field.
My template code is below:
#{
string model = Model ?? string.Empty;
string zipFirst = "";
string zipSecond = "";
if(!String.IsNullOrEmpty(model) && !String.IsNullOrWhiteSpace(model))
{
var values = model.Split('-');
if(values.Count() == 2)
{
zipFirst = values[0] ?? string.Empty;
zipSecond = values[1] ?? string.Empty;
}
}
var pName = ViewData.ModelMetadata.PropertyName;
var zipAreaId = "#" + pName + "zipcodearea";
}
<div id="#(pName)zipcodearea">
<input type="text" maxlength="5" id="zipcodefirst" name="#(pName)codefirst" value="#(zipFirst)" style="width:136px"/> -
<input type="text" maxlength="4" id="zipcodesecond" name="#(pName)codesecond" value="#(zipSecond)" style="width:120px"/>
#Html.HiddenFor(m => m, new { #class = "generatedZipField"})
</div>
<script type="text/javascript">
jQuery(function ($) {
$('#(zipAreaId) #zipcodefirst').autotab({ target: '#(pName)codesecond', format: 'numeric' });
$('#(zipAreaId) #zipcodesecond').autotab({ previous: '#(pName)codefirst', format: 'numeric' });
$('#(zipAreaId)').zipcode();
});
</script>
And i use it like this:
[UIHint("ZipCode")]
[Display(Name = "Zip Code")]
public string Zip { get; set; }
Like you see in my template i got two fields whats not included in the model.
It is #zipcodefirst and #zipcodesecond.
What i need to achieve is to have two separate fields for full us zip code.
When user fill both fields im using jquery widget for merging them into one string and inserting it into hidden field in template. after form submited value in hidden field getting sent into server.
Whats the problem?
I need to add mvc unobtrusive validation for them two fields whats not in the model #zipcodefirst and #zipcodesecond.
validation rules
zipcodefirst field must be filled in first
then zipcodefirst field is filled you can fill second field
second field must have 4 digits in it
first field must have five digits
cant fill second field while first one is empty or incorectly filled
Im strugling with validation part for quite a while now.... :(
How i could achieve that thru mvc3 unobtrusive validation tools?
any help will be highly apreciated guys.
Add unobrusive data data validation on the textbox by adding data-val="true" and use a regular expression for your zip code.
<input type="text" data-val="true" data-val-regex="Invalid zip code format" data-val-regex-pattern="YOUR REGEXP HERE" />
UPDATE
If you also want it to be required you can add the data-val-required attribute.
<input type="text" data-val="true" data-val-requred="Zip code is required" data-val-regex="Invalid zip code format" data-val-regex-pattern="YOUR REGEXP HERE" />
More information about validation in MVC 3:
http://bradwilson.typepad.com/blog/2010/10/mvc3-unobtrusive-validation.html
[Required(ErrorMessage = "ONLY TYPE IN NUMBERS!")]
[Display(Name = "Telefono Fijo")]
public int Telephone { get; set; }
Basically, I'd like that when someone types in a letter, that text up there should display.
Here's my view:
<div>
#Html.LabelFor(model => model.RegisterModel.Telephone)
#Html.EditorFor(model => model.RegisterModel.Telephone)
#Html.ValidationMessageFor(model => model.RegisterModel.Telephone)
</div>
When I type in letters, I get:
"The field Telefono Fijo must be a number."
And when I don't type in ANYTHING, I get:
"ONLY TYPE IN NUMBERS!"
Any ideas? I only want the custom message to show.
You should use RegularExpressionAttribute:
[RegularExpression(#"^\d+$", ErrorMessage = "ONLY TYPE IN NUMBERS!")]