I am trying to build a custom validator in Blazor based on another field on the form. Requirement is to make Phone number mandatory when user checks Receive Text Messages checkbox. I googled a lot but was only able to find custom validator verifying empty or some hardcoded string. Is there a way I can validate a field based on another field's value in Blazor?
PhoneNumberValidator:
public class PhoneNumberValidaton : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
{
return new ValidationResult("Phone Number is mandatory if you want to receive text messages", new[] { validationContext.MemberName });
}
return null;
}
}
Model:
public class UserModel
{
public int Id { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[PhoneNumberValidaton]
[Display(Name = "Phone Number")]
public string Phone { get; set; }
[Required]
[Display(Name = "Receive Text Messages")]
public bool CanReceiveText { get; set; }
}
Requirement is to make Phone number mandatory when user checks Receive Text Messages checkbox
You can create a validation attribute to check for another property's value, if it matches the target value, then the property is required. For example if CanReceiveText
value is true
, then make the Phone
property required.
[RequiredIfAttribute("CanReceiveText", true)]
[Display(Name = "Phone Number")]
public string Phone { get; set; }
[Required]
[Display(Name = "Receive Text Messages")]
public bool CanReceiveText { get; set; }
public class RequiredIfAttribute : ValidationAttribute
{
readonly RequiredAttribute _innerAttribute = new RequiredAttribute();
private string _dependentProperty { get; }
private object _targetValue { get; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
_dependentProperty = dependentProperty;
_targetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectType.GetProperty(_dependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if ((dependentValue == null && _targetValue == null) || dependentValue.Equals(_targetValue))
{
if (!_innerAttribute.IsValid(value))
{
var name = validationContext.DisplayName;
var specificErrorMessage = ErrorMessage;
if (string.IsNullOrEmpty(specificErrorMessage))
specificErrorMessage = $"{name} is required.";
return new ValidationResult(specificErrorMessage, new[] { validationContext.MemberName });
}
}
return ValidationResult.Success;
}
return new ValidationResult(FormatErrorMessage(_dependentProperty));
}
}
Online Demo:
https://blazorfiddle.com/s/p5zfylbm
Working Example:
@page "/"
@using System.ComponentModel.DataAnnotations
<EditForm Model="@userModel" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator/>
<ValidationSummary/>
<p>
<label for="FirstName">FirstName: </label>
<InputText id="FirstName" @bind-Value="userModel.FirstName"/>
<ValidationMessage For="() => userModel.FirstName"/>
</p>
<p>
<label for="LastName">LastName: </label>
<InputText id="LastName" @bind-Value="userModel.LastName"/>
<ValidationMessage For="() => userModel.LastName"/>
</p>
<p>
<label for="Phone">Phone Number: </label>
<InputText id="Phone" @bind-Value="userModel.Phone"/>
<ValidationMessage For="() => userModel.Phone"/>
</p>
<p>
<label for="CanReceiveText">Receive Text Messages: </label>
<InputCheckbox id="CanReceiveText" @bind-Value="userModel.CanReceiveText"/>
<ValidationMessage For="() => userModel.CanReceiveText"/>
</p>
<button type="submit">Submit</button>
</EditForm>
@code {
readonly UserModel userModel = new UserModel();
private void HandleValidSubmit()
{
// Save the data
}
public class UserModel
{
public int Id { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[RequiredIfAttribute("CanReceiveText", true)]
[Display(Name = "Phone Number")]
public string Phone { get; set; }
[Required]
[Display(Name = "Receive Text Messages")]
public bool CanReceiveText { get; set; }
}
public class RequiredIfAttribute : ValidationAttribute
{
readonly RequiredAttribute _innerAttribute = new RequiredAttribute();
private string _dependentProperty { get; }
private object _targetValue { get; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
_dependentProperty = dependentProperty;
_targetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectType.GetProperty(_dependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if ((dependentValue == null && _targetValue == null) || dependentValue.Equals(_targetValue))
{
if (!_innerAttribute.IsValid(value))
{
var name = validationContext.DisplayName;
var specificErrorMessage = ErrorMessage;
if (string.IsNullOrEmpty(specificErrorMessage))
specificErrorMessage = $"{name} is required.";
return new ValidationResult(specificErrorMessage, new[] { validationContext.MemberName });
}
}
return ValidationResult.Success;
}
return new ValidationResult(FormatErrorMessage(_dependentProperty));
}
}
}