0

Coming from winforms, I am quite new to MVC and learning. So pls bear with me. I am attempting to do some validations the right way in MVC. I have some code which does using JS, but I would like to modify that to validate using MVC.

This is my Model

public class MyViewModel
{

    /// <summary>
    /// Start Date
    /// </summary>
     [DataType(DataType.Date)] 
    [DisplayFormat(DataFormatString = "{0:d}",ApplyFormatInEditMode=true)]
    [Display(ResourceType = typeof(UIResources.GeneralPurpose), Name = "StartDate")]
    public DateTime StartDate { get; set; }

    /// <summary>
    /// End Date
    /// </summary>
     [DataType(DataType.Date)]
     [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
     [Display(ResourceType = typeof(UIResources.GeneralPurpose), Name = "EndDate")]
    public DateTime EndDate { get; set; }
}

This is how my html looks

@model MyViewModel
@using ...
{
    <div id="ErrMsg" class="validation-summary-errors center">
    </div>

    using (Ajax.BeginForm("someAction", "someController", new AjaxOptions()))
    {

        @Html.ValidationSummary()

        <fieldset class="control-set-container no-border">
            <ul class="undecorated-list inline-list-items">
                <li>
                    @Html.LabelFor(model => model.StartDate)
                    @Html.MyDateTimePicker(model => model.StartDate)
                </li>
                <li>
                    @Html.LabelFor(model => model.EndDate)
                    @Html.MyDateTimePicker(model => model.EndDate)
                </li>
            </ul>
        </fieldset>
    }

@Html.MyDateTimePicker writes some HTML that gives out a text box and a datepicker control (Text box used to display date in some date format).

Here is what I am trying to validate,

  1. If date is not in correct format (dd/MM/yyyy), the errMsg.Html should be set to the message specified in the view model.
  2. If the startDate is greater than endDate, then display message in errMsg.Html.
  3. While I am at it, I would like to is verify if the date is in one of 2 different formats (dd/MM/yyyy or M/d/yyyy), display the error message when validation fails.
Yogi
  • 9,174
  • 2
  • 46
  • 61
ShaQ.Blogs
  • 1,264
  • 4
  • 17
  • 30
  • Firstly you need `@Html.ValidationMessageFor(m => m.StartDate)` to display the validation error messages. (1) If you want to override the default error message for an invalid value, refer [this answer](http://stackoverflow.com/questions/6214066/how-to-change-default-validation-error-message-in-asp-net-mvc). (2) Use a [foolproof](http://foolproof.codeplex.com/) `[GreaterThan]` or similar conditional validation attribute. (3) Not possible - how would the controller know if `16/04/2016` is a valid date if you were to allow both formats. –  Apr 16 '16 at 06:16
  • But you have not shown the code for `MyDateTimePicker()` so we cannot possibly know if that is generating the correct html with the appropriate `data-val-*` attributes necessary for client side validation. And including `ApplyFormatInEditMode = true` in your `DisplayFormatAttribute` is pointless since its only respected in the `EditorFor()` method, and it needs to be `DataFormatString = "{0:yyyy-MM-dd}"` in order to work anyway. –  Apr 16 '16 at 06:17

3 Answers3

1

MVC Allows yout to create custom validation attributes, by using either the Remote attribute, or by creating a new attribute.

The Remote attribute allows you to make jquery send an Ajax call to a specified method during field validation, when the onfocusout event will be throwned.

For example, I have a method in my UserController that makes sure that the same UserId is not used twice in the application:

[Remote("UnicityUserId", "User", ErrorMessageResourceName = "ErrorUnicity", ErrorMessageResourceType = typeof(Resources.Resources), AdditionalFields = "ID")]
public string UserId {get;set;}

Then, in my controller:

public JsonResult UnicityUserId(string UserId, string ID)
{
    int id = 0;
    Int32.TryParse(ID, out id);
    bool ok = false;

    if (!String.IsNullOrEmpty(UserId))
        ok = !UserDb.Exist(this.db, this.Tracer, UserDb.UserId, UserId, id); //Checks wether there is already another user with the same UserId

    return (Json(ok, JsonRequestBehavior.AllowGet));
}

More info here

The other validation mode is performed when MVC will receive your POST data and will try to bind it to your model.

For this method, you will have to create your own attribute.

Still from the unicity example:

public class UnicityAttribute : ValidationAttribute

this class will have to implement a method called IsValid that will perform validation and return a ValidationResult to MVC's ModelBinder

To make sure MVC will call it, just put the attribute on your field:

[UnicityAttribute]
public string UserId {get;set;}

More info here

Waterfrag
  • 517
  • 4
  • 20
  • Thanks for the links Marrow. Leaving simple validations aside, I notice that the example in links mention of server side validation (without having to refresh the entire page). Is this typical of MVC to use server side validation using C# & MVC, instead of client side using JS? So there are always calls back to server. – ShaQ.Blogs Apr 15 '16 at 10:17
  • I d'ont know if it's typical, but making server side validation makes the code easier to read (View contain less JS), and a lot of things are easier to manage in c# than in JS (Dates, decimals, etc...). – Waterfrag Apr 15 '16 at 12:01
0
    enter code here
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace Attributes
{
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class MinimumPaymentDateAttribute : ValidationAttribute, IClientValidatable
    {
        private readonly string errorMessage;
        public string ValidationErrorMessage { get { return errorMessage; } }
        /// <summary>
        /// Constructor for intializing variables
        /// </summary>
        public MinimumPaymentDateAttribute()
        {
        }

        /// <summary>
        ///  Constructor for intializing variables
        /// </summary>
        /// <param name="validationErrorMessage"></param>
        public MinimumPaymentDateAttribute(string validationErrorMessage)
        {
            this.errorMessage = validationErrorMessage;
        }
        /// <summary>
        /// Validation for of Rule implement Client Validation
        /// </summary>
        /// <param name="value"></param>
        /// <param name="validationContext"></param>
        /// <returns></returns>
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (null != validationContext)
            {
                PaymentInformation paymentInfo = (PaymentInformation)validationContext.ObjectInstance;
                if (value != null && validationContext != null)
                {
                    if (paymentInfo.PaymentDate < paymentInfo.MinDate)
                    {
                        return new ValidationResult(this.ValidationErrorMessage);
                    }
                }
            }
            return ValidationResult.Success;
        }

        /// <summary>
        ///  Validation rules client side for Minimum Date
        /// </summary>
        /// <param name="metadata"></param>
        /// <param name="context"></param>
        /// <returns>IEnumerable<ModelClientValidationRule></returns>
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            string errorMessage = this.FormatErrorMessage(metadata.DisplayName);
            ModelClientValidationRule MinDateRule = new ModelClientValidationRule();
            DateTime? validationData = GetValidationParameters(context.Controller.ViewData.Model);
            MinDateRule.ErrorMessage = errorMessage;
            MinDateRule.ValidationType = "validminpaymentdate";(Java script function name)
            MinDateRule.ValidationParameters.Add("mindate", validationData);
            yield return MinDateRule;
        }


        /// <summary>
        ///  Checking for Model type for handling Different ViewData
        /// </summary>
        /// <param name="model"></param>   
        /// <returns>string[]</returns>
        private static DateTime? GetValidationParameters(object model)
        {
            DateTime? MinDate=null;
            if (model.GetType() == typeof(PaymentInformation))
            {
                PaymentInformation obj = (PaymentInformation)model;
                return obj.MinDate;
            }
            else if (model.GetType() == typeof(PaymentViewModel))
            {
                PaymentViewModel obj = (PaymentViewModel)model;
                return obj.PaymentInfo.MinDate;
            }
            return MinDate;
        }
    }
}


$.validator.addMethod("validminpaymentdate", function (value, element, params) {
    var valid = true;
    var mindate = new Date(params);
    var valdate = new Date(value);
    if (valdate < mindate) {
        valid = false;
    }
    return valid;

});
$.validator.unobtrusive.adapters.addSingleVal("validminpaymentdate", "mindate");
-1
function checkdate(input) {

    //var validformat = /^\d{4}\-\d{2}\-\d{2}$/;
    var validformat = /^(\d{4})-(\d{2})-(\d{2})$/;  //Basic check for format validity
    if (input.val() != "") {
        if (!validformat.test(input.val()))
            return false;
        else { //Detailed check for valid date ranges
            var monthfield = input.val().split("-")[1];
            var dayfield = input.val().split("-")[2];
            var yearfield = input.val().split("-")[0];
            var dayobj = new Date(yearfield, monthfield - 1, dayfield);
            if ((dayobj.getMonth() + 1 != monthfield) || (dayobj.getDate() != dayfield) || (dayobj.getFullYear() != yearfield))
            //alert("Invalid Date Format.");
                return false;
            else
                return true;
        }
    }
    else {
        return true;
    }

}

The paramiter value 'input' will take the object of datetime control

var obj=$('#txtDatetime');

Call - CheckDate(obj); where you want to validate

Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
Aswini
  • 14
  • 3
  • Did you even read the question? The OP isn't asking for javascript validation at all (not even in the tags) – Jcl Apr 15 '16 at 09:35
  • I already have JS, not looking for more JS. MVC mainly revolves around keeping the view as simple as possible. – ShaQ.Blogs Apr 15 '16 at 09:48
  • Ok, Better you use data annotation Apply DataAnnotation like: [DisplayFormat(DataFormatString = "{0:MMM dd, yyyy}")] – Aswini Apr 15 '16 at 10:39
  • it is good practice to have js. if you think that your view will be heavy that is wrong idea. because you have bundling in mvc that is not dependent on view's script load. – Aswini Apr 15 '16 at 10:45