keeehlan
keeehlan

Reputation: 8054

Compare attribute does not use Display attribute of compared property

I have a ViewModel used for changing a password, and it uses the Compare DataAnnotation like so:

[Display(Name = "New Password")]
public string New { get; set; }

[Compare("New")]
[Display(Name = "Confirm Password")]
public string ConfirmPassword { get; set; }

Unfortunately the Compare attribute does not utilize the Display attribute of the compared property.

The error message displays as

'Confirm Password' and 'New' do not match.

which you can see uses the comparing property's Display attribute, but not the compared property's.

I'll also specify that I don't want to use the ErrorMessage parameter because then I'd be hard-coding the property name rather than simply acquiring it from an existing attribute. I'd like to keep this solution as best-practice as possible.

How can I make the Compare attribute utiliize the Display attribute of the compared property?

Upvotes: 1

Views: 1401

Answers (2)

Ro Dev
Ro Dev

Reputation: 21

Maybe too late, but I had the same issue in Asp.Net Core Razor Page Web App, and the easy fix was to make a new InputModel class with the Password and ConfirmPassword properties.Then, bind the form inputs to the InputModel properties.

Like this:

    [BindProperty]
    public InputModel UserPassword { get; set; }

    public class InputModel {
      [BindProperty]
      [Display(Name = "Contraseña")]
      [Required(ErrorMessage = "La contraseña es obligatoria.")]
      [RegularExpression("^[a-zA-ZñÑ0-9]+$",ErrorMessage = "Sólo se permiten letras y números.")]
      [StringLength(12,ErrorMessage = "La contraseña no debe tener más de 12 caracteres.")]
      [MaxLength(12,ErrorMessage = "La contraseña no debe tener más de 12 caracteres.")]
      [MinLength(2,ErrorMessage = "La contraseña no debe tener menos de 2 caracteres.")]
      public string Password { get; set; }

      [BindProperty]
      [Display(Name = "Confirmación de Contraseña")]
      [Required(ErrorMessage = "La contraseña es obligatoria.")]
      [Compare(nameof(Password),ErrorMessage = "La contraseña de confirmación no coincide.")]
      public string ConfirmPassword { get; set; }
    }

Upvotes: 2

Melvin DVaz
Melvin DVaz

Reputation: 1236

I think that this may be some issue with the Compare attribute, since you can see an OtherDisplayName attribute in its list of properties, and it correctly uses the Display Name for the property it is decorating ("Confirm Password" and not "ConfirmPassword").

One work around I have found is to simply create a new class that inherits from CompareAttribute, like so:

public class CompareWithDisplayName : CompareAttribute
{
    public CompareWithDisplayName(string otherProperty) : base(otherProperty)
    {
    }
}

Then use it on your property:

[Display(Name = "New Password")]
public string New { get; set; }


[Display(Name = "Confirm Password")]
[CompareWithDisplayName("New")]
public string ConfirmPassword { get; set; }

I honestly have no idea why this works. It could be that it is something to do with reflection or the order in which it works out what each property's display attribute is. By creating a custom version of it, perhaps the ordering is changed? Either way, this did the trick for me :)

Edit 2 Sorry, forgot to add the extra part needed for client side validation, which is explained here. You can either add this in your Global.asax.cs file:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(CompareWithDisplayName), typeof(CompareAttributeAdapter))

or implement the IClientValidatable interface on the custom attribute. Both of these are shown in the link

Upvotes: 2

Related Questions