2010-01-25 11 views
8

En el sitio que estoy construyendo necesito tener las propiedades de fecha y hora divididas en diferentes combinaciones dependiendo de la propiedad. Ejemplos:ASP.NET MVC 2 Plantillas de editor personalizadas para dividir campos de fecha y hora

La vista de miembro tiene una propiedad de fecha de nacimiento que debe mostrarse en la vista como menús desplegables día/mes/año separados.

Una vista de tarjeta de crédito tiene una propiedad de fecha de caducidad que debe mostrarse como listas desplegables de mes/año separadas.

Una vista de excursión tiene una propiedad de tiempo único donde se necesitan horas y minutos separados como cuadros de texto.

Cada uno de estos escenarios requiere validación e idealmente también la validación del lado del cliente.

He examinado varias opciones, como el enlace personalizado, los atributos personalizados y ahora estoy buscando plantillas personalizadas de editor, pero hasta ahora he tenido poca suerte en encontrar las soluciones adecuadas.

Parece una tarea común, pero la búsqueda en la red ha demostrado poco que cubre todo (especialmente con el elemento de validación).

Así que mi pregunta es si alguien más ha logrado lograr lo anterior.

(dedos cruzados!)

+0

"pero hasta ahora he tenido poca suerte en encontrar soluciones adecuadas". ¿Qué significa eso? – jfar

+0

Hola jfar, soy bastante nuevo en C# y mvc y, por lo tanto, he confiado en las búsquedas de Google para encontrar un buen punto de partida para una solución. El principal ha sido http://www.hanselman.com/blog/SplittingDateTimeUnitTestingASPNETMVCCustomModelBinders.aspx pero no he podido encontrar la manera de agregar validación (del lado del servidor o del lado del cliente). –

+0

Oh bien, pensé que era otra cosa. A menos que alguien más responda, publicaré una respuesta esta noche. Hago este tipo de cosas todo el tiempo. – jfar

Respuesta

13

Bien, voy a intentar conseguirte el 90% del camino. Esta es en realidad una parte enorme y compleja de MVC 2 y casi imposible de responder en este cuadro de respuesta.

Ahora primero debe ir al blog de Brad Wilsons y leer en profundidad sobre cómo personalizar las plantillas predeterminadas de MVC 2. Eso debería darte una comprensión mucho más clara de todas las partes móviles.

http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html

Ahora voy a empezar un ejemplo sencillo de cómo crear una vista de cita modelo artificial donde queremos para asegurarse de que los valores proporcionados no retroceder en el tiempo. No prestes atención a los atributos en este momento, llegaremos allí.

Aquí es el modelo de vista que estoy usando:

public class AppointmentViewModel 
{ 
    [Required] 
    public string Name { get; set; } 

    [CantGoBackwardsInTime] 
    public DateRange DateRange { get; set; } 
} 

public class DateRange 
{ 
    public DateTime Start { get; set; } 
    public DateTime End { get; set; } 

    [Required] 
    public int Price { get; set; } 
} 

Y He añadido esto a la HomeController defecto (nada de fantasía):

public ActionResult Appointment() 
    { 
     return View(new AppointmentViewModel()); 
    } 

    [HttpPost] 
    public ActionResult Appointment(AppointmentViewModel appointment) 
    { 
     return View(appointment); 
    } 

Y aquí es mi punto de vista:

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> 
Appointment 
</asp:Content> 

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> 
    <h2>Add Appointment</h2> 
    <%= Html.ValidationSummary() %> 
    <% using(Html.BeginForm()) { %> 
    <%= Html.EditorForModel() %> 
    <input type="submit" value="Save Changes" /> 
    <%} %> 
</asp:Content> 

Paso 1: Configuración del escenario

Lo primero que debe hacer es tomar las "plantillas predeterminadas" de la entrada del blog. El más importante en este caso es el que se ubicará en /Views/Shared/EditorTemplates/Object.asxc Object.ascx es la piedra angular de toda la operación. Todos los métodos Html.Editor ***** llamarán esto eventualmente.

Ahora la primera pieza de funcionalidad por defecto que tenemos que cambiar es esta línea dentro de Object.ascx

<% if (ViewData.TemplateInfo.TemplateDepth > 1) { %> 
    <%= ViewData.ModelMetadata.SimpleDisplayText%> 
<% } 

Lo que eso es decir es "no muestra ningún tipo complejos anidados" y que no queremos ese. Cambie eso> 1 a a> 2. Ahora, los modelos de vista en su gráfico de objetos tendrán plantillas creadas para ellos en lugar de simplemente crear texto de marcador de posición.

Simplemente mantenga todo lo demás predeterminado por el momento.

* Paso 2: Sustitución de plantillas **

Si usted lee las entradas de blog espero que pueda entender ahora cómo el Editor *** y métodos de visualización llamarán automáticamente las plantillas en Vista/Shared/EditorTemplates y DisplayTemplates . Piense en ellos como si llamaran a Html.RenderPartial ("TYPENAME", MyType) no lo son pero está lo suficientemente cerca en concepto.

Así que si ejecuta la solución hasta aquí y va a la url correcta, notará que MVC 2 llamará a Object.ascx dos veces, una para su AppointmentViewModel y otra para la propiedad DateRange. Fuera de la caja solo se renderiza la misma colección de campos de formulario.

Digamos que queremos hacer que nuestra plantilla rodee nuestro editor DateRange con un recuadro con borde rojo. Lo que queremos hacer es un MVC 2 cortocircuito para llamar a una plantilla personalizada DateTime.ascx en lugar de Object.ascx y eso es tan fácil como agregar nuestra propia plantilla en View/Shared/EditorTemplates/DateRange.ascx. En este caso, sólo he tomado lo que fue generado por Object.ascx trabajar con nuestro modelo DateRange y acaba de pegar el código en un nuevo DateRange.ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> 
<div style="border: 1px solid #900"> 
    <div class="editor-label"><label for="DateRange">DateRange</label></div>    
     <div class="editor-field">    
      <div class="editor-label"><label for="DateRange_Start">Start</label> 
     </div> 


     <div class="editor-field"> 
      <input class="text-box single-line" id="DateRange_Start" name="DateRange.Start" type="text" value="" />    
     </div> 

     <div class="editor-label"><label for="DateRange_End">End</label></div> 

     <div class="editor-field"> 
      <input class="text-box single-line" id="DateRange_End" name="DateRange.End" type="text" value="" />    
     </div> 

     <div class="editor-label"><label for="DateRange_Price">Price</label></div> 

     <div class="editor-field"> 
      <input class="text-box single-line" id="DateRange_Price" name="DateRange.Price" type="text" value="" /> 

     </div>  
    </div> 
</div> 

Wala!

Ahora, cuando ejecute la solución, debería ver un recuadro rojo alrededor de nuestro DateRange. ¡El resto de las personalizaciones dependen de ti! Puede agregar recuadros jQuery datepicker. En tu caso, puedes poner ambos campos en un solo div para que se alineen horizontalmente. El cielo es el límite en este punto.

Paso 3: Validación:

Validación trabaja más o menos de la manera que cabría esperar. Un atributo [Requerido] dentro de su tipo de DateRange funciona igual que cualquier otro atributo de validación.

Ahora ves que hice un atributo no puedo ir hacia atrás en el tiempo que he puesto en la propiedad DateRange de AppointmentViewModel. Todo lo que tiene que hacer para crear este tipo atributos de validación específico es heredar y poner en práctica el ValidationAttribute de base:

public class CantGoBackwardsInTime : ValidationAttribute 
{ 
    public override string FormatErrorMessage(string name) 
    { 
     return "Your date range can't go backwards in time"; 
     //return base.FormatErrorMessage(name); 
    } 

    public override bool IsValid(object value) 
    { 
     if (!(value is DateRange)) 
      throw new InvalidOperationException("This attributes can only be used on DateRange types!"); 

     var dateRange = value as DateRange; 

     return dateRange.End > dateRange.Start; 
    } 
} 

Ahora bien, si se agrega esto y decorar su propiedad debería ver el mensaje de error que aparece en el atributo de validación CantGoBackwardsInTime personalizado.

Voy a actualizar y aclarar más de esto si tiene algún problema, pero esto debería comenzar y en su camino. (Pensé que podría golpear esto antes de dormir) Solo una advertencia: el nuevo Editor para piezas de MVC 2 es lo más impresionante del mundo y tiene un gran potencial para brindar capacidades de MVC 2 super RAD; sin embargo, hay poco que saber sobre información además del blog de Brad Wilson. Solo sigue y no tengas miedo de mirar el código fuente de MVC 2 si lo necesitas también.

+0

Gracias por este jfar, sin duda me ha dado algo para trabajar, pero estoy más interesado en dividir el objeto datetime actual en sus entradas componentes (una entrada separada por año, mes y día, por ejemplo) en lugar de múltiples datetime completo instancias. ¿Crees que este es el camino correcto a seguir? ¿Qué le parecería agregar la validación del lado del cliente como parte de esta solución? –

+0

Bien, bien. Simplemente deberá crear su propia plantilla de DateTime que dividirá la UI. En cuanto a la validación del lado del cliente, simplemente incluya el javascript en su plantilla personalizada de DateTime.ascx. – jfar

+0

Ok, lo tengo trabajando con una de las combinaciones que estaba buscando (mes y año solo en dos entradas separadas), así que muchas gracias por su ayuda y marcaré esto como respondido. Una cosa que me pregunto ahora es que lo tengo para que un atributo de validación personalizado tome un objeto CardDate (int mes e int año) e intente formar una fecha válida. si falla, devuelve correctamente el falso indicando que la fecha de caducidad no es válida. Una cosa, ¿es posible desde dentro del atributo hacer que ambos campos dentro del objeto de tipo tarjeta sean inválidos para que se muestren en un estado inválido en la vista? Espero que tenga sentido! –

0

¿Estás tratando de hacerlo todo en una página? O un método? No estoy muy seguro de cómo quieres hacer esto, así que aquí está mi oportunidad, si te entiendo bien.

Así que, por ejemplo, podría hacer algo como esto para la fecha de nacimiento.

En su opinión

<%= Html.DropDownList("Birthday")%> 
<%= Html.DropDownList("BirthMonth")%> 
<%= Html.DropDownList("BirthYear")%> 

En su controlador en algún lugar tienen algo como esto. Tal vez podrías combinar todo en un ciclo o algo así.

List<int> month = new List<int>() 
for(int i = 0; i < 12, i++) 
{ 
    month.add(i + 1); 
} 

ViewData["BirthMonth"] = new SelectList(month); 


int days = DateTime.DaysInMonth(Year, Month); 

    // do a another for loop add it to viewsData = ViewData["Birthday"] . 
    // do a another for loop for years and add it do another viewdata = ViewData["BirthYear"]. 

Así que lo que quiero decir es que su puede hacer algunas cosas en el servidor para obtener las fechas que desee y simplemente agregarlo a través ViewData en las listas desplegables.

+0

Hola, chobo2, gracias por la entrada, pero esto no va realmente en la dirección correcta. Estoy buscando una forma de enlazar a una propiedad datetime en un modelo y todavía validar, etc. El enlace en el comentario anterior debería dar una mejor idea de lo que estoy tratando de lograr. –

Cuestiones relacionadas