Vincent
Vincent

Reputation: 762

ASP.NET MVC how to achieve to use the same model with different error message

I am having this issue at the moment, I had address model (use required attribute to decorate) which can be used more than once on the same page, one is billing address and the other one is shipping address. when validation failed, I'd like to have suffix in front of my generic error message indicate which address is required e.g. "{0} - address line 1 required", either billing or shipping

Here is my model

   public class AddressBaseModel
   {                            
        [Display(Name="Address line 1")]
        [Required(ErrorMessageResourceType = typeof(ModelValidation), ErrorMessageResourceName = "AddrLine1Required")]
        public string AddressLine1 { get; set; }


        [Display(Name="Address line 2")]
        [Required(ErrorMessageResourceType = typeof(ModelValidation), ErrorMessageResourceName = "AddrLine2Required")]
        public string AddressLine2 { get; set; }

        [Display(Name="Address line 3")]
        public string AddressLine3 { get; set; }

        [Display(Name="Address line 4")]
        public string AddressLine4 { get; set; }
       }
    }

Here is the code segment I used in my page

<fieldset class="space-bottom">
     <legend>Please enter your home address</legend>
         <div id="home_fields">
             @Html.EditorFor(m => m.HomeAddress)
          </div>
 </fieldset>

  <fieldset class="space-bottom">
      <legend>Please enter your delivery address</legend>
          <div id="delivery_fields">
              @Html.EditorFor(m => m.DeliveryAddress)
          </div>
  </fieldset>

Thanks

Upvotes: 0

Views: 741

Answers (3)

Robert Corvus
Robert Corvus

Reputation: 2136

You could create a custom attribute that does the dynamic formatting for you. You would just tag your address fields with the Address attribute like this:

    [Address]
    public string AddressLine1 { get; set; } 

You would need to add a property in the AddressBaseModel where you tell the system what type of address this is (you would set this to "Billing" or "Shipping" when you instantiate the view model right before you pass the view model to the View in the controller get action):

public string AddressType { get; set; }

A custom attribute like this should work (I haven't tested it, I wrote it just now). This automatically gets the address type you specified when you create the model instance and formats it with the display name of the address field).

public class AddressAttribute : ValidationAttribute
{
    private const string DefaultErrorMessage = "{0} - {1} required";

    public AddressAttribute()
        : base(DefaultErrorMessage) { }

    protected override ValidationResult IsValid(object value,
                                            ValidationContext validationContext)
    {
        if (value != null)
        {
            if (!base.IsValid(value))
            {
                // get the property called "AddressType" from the model so we know if it's Billing or Shipping
                var addressType = validationContext.ObjectInstance.GetType()
                                                .GetProperty("AddressType")
                                                .GetValue(validationContext.ObjectInstance, null);

                // use the display name of the address field in the error message
                return new ValidationResult(
                    string.Format(DefaultErrorMessage, addressType, validationContext.DisplayName));
            }
        }

        return ValidationResult.Success;
    }
}

Upvotes: 0

Darin Dimitrov
Darin Dimitrov

Reputation: 1039110

Personally I use the FluentValidation.NET library instead of Data Annotations as it makes things so much easier and provides a lot more power. Here's an example of how to achieve your goal using this ilbrary.

  1. Create a new ASP.NET MVC 3 project using the default Visual Studio template
  2. Install the FluentValidation.MVC3 NuGet package.
  3. Add the following line to Application_Start:

    ModelValidatorProviders.Providers.Add(
        new FluentValidationModelValidatorProvider(
            new AttributedValidatorFactory()
        )
    );
    
  4. Define the following models:

    public class AddressBaseModel
    {
        public string AddressLine1 { get; set; }
    }
    
    [Validator(typeof(MyViewModelValidator))]
    public class MyViewModel
    {
        public AddressBaseModel HomeAddress { get; set; }
        public AddressBaseModel DeliveryAddress { get; set; }
    }
    
  5. And the following Validators:

    public class AddressBaseModelValidator : AbstractValidator<AddressBaseModel>
    {
        private readonly string _addressType;
    
        public AddressBaseModelValidator(string addressType)
        {
            _addressType = addressType;
            RuleFor(x => x.AddressLine1)
                .NotEmpty()
                .WithMessage(string.Format("{0} - address line 1 required", addressType));
        }
    }
    
    public class MyViewModelValidator : AbstractValidator<MyViewModel>
    {
        public MyViewModelValidator()
        {
            RuleFor(x => x.HomeAddress)
                .SetValidator(new AddressBaseModelValidator("billing"));
    
            RuleFor(x => x.DeliveryAddress)
                .SetValidator(new AddressBaseModelValidator("shipping"));
    
        }
    }
    
  6. Modify the HomeController:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var model = new MyViewModel
            {
                HomeAddress = new AddressBaseModel(),
                DeliveryAddress = new AddressBaseModel()
            };
            return View(model);
        }
    
        [HttpPost]
        public ActionResult Index(MyViewModel model)
        {
            return View(model);
        }
    }
    
  7. And the corresponding Index.cshtml view:

    @model MyViewModel
    
    @using (Html.BeginForm())
    {
        <fieldset class="space-bottom">
            <legend>Please enter your home address</legend>
            <div id="home_fields">
                @Html.EditorFor(m => m.HomeAddress)
            </div>
        </fieldset>
    
        <fieldset class="space-bottom">
            <legend>Please enter your delivery address</legend>
            <div id="delivery_fields">
                @Html.EditorFor(m => m.DeliveryAddress)
            </div>
        </fieldset>
    
        <input type="submit" value="Register" />
    }
    

Upvotes: 1

BentOnCoding
BentOnCoding

Reputation: 28208

This should work:

 [Required(ErrorMessage = "The Address 2 is required.")]

Upvotes: 0

Related Questions