2011-08-11 64 views
62

He creado un ValidationAttribute personalizado que compara 2 fechas y se asegura de que la segunda fecha es mayor que la primera:MVC validación personalizada: comparar dos fechas

public sealed class IsDateAfter : ValidationAttribute, IClientValidatable 
{ 
    private readonly string testedPropertyName; 
    private readonly bool allowEqualDates; 

    public IsDateAfter(string testedPropertyName, bool allowEqualDates = false) 
    { 
     this.testedPropertyName = testedPropertyName; 
     this.allowEqualDates = allowEqualDates; 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     var propertyTestedInfo = validationContext.ObjectType.GetProperty(this.testedPropertyName); 
     if (propertyTestedInfo == null) 
     { 
      return new ValidationResult(string.Format("unknown property {0}", this.testedPropertyName)); 
     } 

     var propertyTestedValue = propertyTestedInfo.GetValue(validationContext.ObjectInstance, null); 

     if (value == null || !(value is DateTime)) 
     { 
      return ValidationResult.Success; 
     } 

     if (propertyTestedValue == null || !(propertyTestedValue is DateTime)) 
     { 
      return ValidationResult.Success; 
     } 

     // Compare values 
     if ((DateTime)value >= (DateTime)propertyTestedValue) 
     { 
      if (this.allowEqualDates) 
      { 
       return ValidationResult.Success; 
      } 
      if ((DateTime)value > (DateTime)propertyTestedValue) 
      { 
       return ValidationResult.Success; 
      } 
     } 

     return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); 
    } 

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) 
    { 
     var rule = new ModelClientValidationRule 
     { 
      ErrorMessage = this.ErrorMessageString, 
      ValidationType = "isdateafter" 
     }; 
     rule.ValidationParameters["propertytested"] = this.testedPropertyName; 
     rule.ValidationParameters["allowequaldates"] = this.allowEqualDates; 
     yield return rule; 
    } 

clase CalendarEntry: ...

public virtual DateTime StartDate { get; set; } 

[IsDateAfter("StartDate", true, ErrorMessage="End date needs to be after start date")] 
public virtual DateTime EndDate { get; set; } 

VISTA:

$.validator.unobtrusive.adapters.add(
    'isdateafter', ['propertytested', 'allowequaldates'], function (options) { 
    options.rules['isdateafter'] = options.params; 
    options.messages['isdateafter'] = options.message; 
}); 
$.validator.addMethod("isdateafter", function(value, element, params) { 
    alert(params.propertytested); 
    var startdatevalue = $('input[name="' + params.propertytested + '"]').val(); 
    if (!value || !startdatevalue) return true; 
    return (params.allowequaldates) ? Date.parse(startdatevalue) <= Date.parse(value) : Date.parse(startdatevalue) < Date.parse(value); 
}, ''); 

Esto funciona bien cuando el CalendarEntry no está incluido dentro de otra clase. Sin embargo, cuando se utiliza un modelo de vista de este modo:

public class TrainingDateEditViewModel 
    { 
     #region Properties 

     /// <summary> 
     /// Gets or sets CalendarEntry. 
     /// </summary> 
     public CalendarEntry CalendarEntry { get; set; } 
.... 

La validación del cliente ya no funciona porque la salida html producido es la siguiente:

<input type="text" value="" name="CalendarEntry.EndDate" id="CalendarEntry_EndDate" data-val-isdateafter-propertytested="StartDate" data-val-isdateafter-allowequaldates="True" data-val-isdateafter="End date needs to be after start date" data-val="true"> 

Y el

data-val-isdateafter-propertytested="StartDate" and IT SHOULD BE: "CalendarEntry.StartDate". 

¿Cómo lo haría Lo hago para que se una a "CalendarEntry.StartDate" rule.ValidationParameters ["propertytested"] = this.testedPropertyName; // AQUÍ DEBE SER EL NOMBRE COMPLETO ??? ¿¿CÓMO??

gracias

+2

gracias por este código, he implementado con éxito en mi validación – Sam

+0

Gracias también, para esto, y @counsellorben para la actualización. –

+2

Puede ser una buena idea Sufrir su clase con 'Attribute' – Liam

Respuesta

31

Es necesario modificar la secuencia de comandos del lado del cliente para comprobar si el prefijo del elemento de prueba, y añadir el prefijo (si los hay) a su selector, de la siguiente manera:

$.validator.addMethod("isdateafter", function(value, element, params) { 
    var parts = element.name.split("."); 
    var prefix = ""; 
    if (parts.length > 1) 
     prefix = parts[0] + "."; 
    var startdatevalue = $('input[name="' + prefix + params.propertytested + '"]').val(); 
    if (!value || !startdatevalue) 
     return true;  
    return (params.allowequaldates) ? Date.parse(startdatevalue) <= Date.parse(value) : 
     Date.parse(startdatevalue) < Date.parse(value); 
}); 
+1

esta es una solución invaluable para todas mis validaciones, gracias – Sam

+0

Funcionó bien ... Gracias amigo – Garry

4

no hacer olvida incluir el lado del cliente dentro de este código. ¡Me llevó horas encontrar que esto faltaba!

(function ($) { 

    // your code here.. 

})(jQuery); 
1

Sólo para arreglar un pequeño error en JavaScript de counsellorben: los "params.allowequaldates()" se interpreta como una cadena (que tendrá un valor de "falso" o "verdadero"), pero esa cuerda siempre será evaluado como verdadero, permitiendo siempre las mismas fechas. Si también desea permitir más niveles de anidamiento de los objetos que sólo 1, por lo que recibirá:

$.validator.addMethod("isdateafter", function(value, element, params) { 
    var parts = element.name.split("."); 
    var prefix = ""; 
    for (var i = 0; i < parts.length - 1; i++) 
     prefix = parts[i] + "."; 
    var startdatevalue = $('input[name="' + prefix + params.propertytested + '"]').val(); 
    if (!value || !startdatevalue) 
     return true;  
    var allowequal = params.allowequaldates.toLowerCase === "true"; 
    return allowequal ? Date.parse(startdatevalue) <= Date.parse(value) : 
     Date.parse(startdatevalue) < Date.parse(value); 
}); 
0

En la última respuesta había algunos paréntesis que falta en la llamada a toLowerCase, aquí es una actualización versión con el documento listo y el .validator.unobtrusive $ ...- parte:

$(function() { 
    $.validator.addMethod("isdateafter", function(value, element, params) { 
     var parts = element.name.split("."); 
     var prefix = ""; 
     for (var i = 0; i < parts.length - 1; i++) { 
      prefix = parts[i] + "."; 
     } 

     var startdatevalue = $('input[name="' + prefix + params.propertytested + '"]').val(); 

     if (!value || !startdatevalue) return true;  

     var allowequal = params.allowequaldates.toLowerCase() === "true"; 
     return allowequal ? Date.parse(startdatevalue) <= Date.parse(value) : 
      Date.parse(startdatevalue) < Date.parse(value); 
    }); 
    $.validator.unobtrusive.adapters.add('isdateafter', 
     ['propertytested', 'allowequaldates'], 
     function (options) { 
      options.rules['isdateafter'] = options.params; 
      options.messages['isdateafter'] = options.message; 
     }); 
});