2010-01-25 13 views
8

Nel sito che sto costruendo ho bisogno di avere proprietà datetime suddivise in combinazioni diverse a seconda della proprietà. Esempi:ASP.NET MVC 2 Modelli di editor personalizzati per la divisione dei campi datetime

La vista membro contiene la data di nascita della proprietà che deve essere visualizzata nella vista come elenchi a discesa di giorno/mese/anno.

Una vista della carta di credito ha una data di scadenza che deve essere mostrata come elenchi a discesa separati di mese/anno.

Una vista escursione ha una proprietà solo tempo in cui sono necessarie ore e minuti separati come caselle di testo.

Ognuno di questi scenari richiede la convalida e idealmente anche la validazione lato client.

Ho esaminato varie opzioni come l'associazione personalizzata, gli attributi personalizzati e ora sto guardando i modelli di editor personalizzati ma finora ho avuto poca fortuna nel trovare soluzioni adeguate.

Sembra un compito comune, ma la ricerca in rete ha mostrato poco che copre tutto (specialmente con l'elemento di convalida).

Quindi la mia domanda è che qualcun altro è riuscito a realizzare quanto sopra?

(dita incrociate!)

+0

"ma finora ho avuto poca fortuna nel trovare soluzioni adeguate." Che cosa significa? – jfar

+0

Ciao jfar, sono abbastanza nuovo per C# e mvc e quindi mi sono affidato alle ricerche di google per trovare un buon punto di partenza di una soluzione. Il principale è stato http://www.hanselman.com/blog/SplittingDateTimeUnitTestingASPNETMVCCustomModelBinders.aspx ma non sono riuscito a capire come aggiungere la convalida (lato server o lato client). –

+0

Oh ok, pensavo fosse qualcos'altro. A meno che qualcun altro risponda, invierò una risposta stasera. Faccio questo tipo di cose tutto il tempo. – jfar

risposta

13

Ok, ho intenzione di provare a ottenere il 90% del modo in cui ci sono. Questa è in realtà una parte enorme e complessa di MVC 2 e quasi impossibile rispondere solo in questa casella di risposta.

Ora per prima cosa dovresti andare al blog di Brad Wilsons e leggere in profondità come personalizzare i modelli MVC 2 predefiniti. Questo dovrebbe darti una comprensione molto più chiara di tutte le parti mobili.

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

Ora inizierò un semplice esempio di come creare una vista del modello appuntamento artificiosa dove vogliamo assicurarsi che i valori forniti non tornare indietro nel tempo. Non prestare attenzione agli attributi in questo momento, ci arriveremo.

Ecco il ViewModel che sto utilizzando:

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; } 
} 

E ho aggiunto questo al HomeController default (niente di speciale):

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

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

Ed ecco la mia 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> 

Passaggio 1: Impostazione dello stage

La prima cosa che vuoi fare è prendere i "modelli predefiniti" dal post di blog. L'importante in questo caso è quello che si troverà in /Views/Shared/EditorTemplates/Object.asxc Object.ascx è la chiave di volta dell'intera operazione. Tutti i metodi Html.Editor ***** lo chiameranno alla fine.

Ora, il primo pezzo di funzionalità predefinita che dobbiamo cambiare è questa linea all'interno del Object.ascx

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

cosa thats dicendo è "non presentano tipi complessi annidati" e non vogliamo quella. Cambia quello> 1 in un> 2. Ora i modelli nel tuo oggetto grafico avranno dei modelli creati per loro invece di creare solo testo segnaposto.

Basta mantenere tutto il resto predefinito per ora.

* Fase 2: Modelli di priorità assoluta **

Se leggete le voci di blog Spero che possiate capire ora come l'Editor *** e metodi di visualizzazione chiamerà automaticamente i modelli nella vista/Shared/EditorTemplates e DisplayTemplates . Pensa a loro come se chiamassero Html.RenderPartial ("TYPENAME", MyType) non lo sono, ma è abbastanza vicino nel concetto.

Quindi se si esegue la soluzione fino a questo punto e si passa all'url corretto, si noterà che MVC 2 chiamerà Object.ascx due volte, una volta per AppointmentViewModel e nuovamente per la proprietà DateRange. Immediatamente viene eseguito il rendering della stessa raccolta di campi modulo.

Diciamo che vogliamo far sì che il nostro modello contenga il nostro editor DateRange con una casella bordata di rosso. Quello che vogliamo fare è short circut MVC 2 per chiamare un modello personalizzato DateTime.ascx invece di Object.ascx e questo è facile come aggiungere il nostro modello in View/Shared/EditorTemplates/DateRange.ascx. In questo caso ho appena preso quello che è stato generato da Object.ascx lavorando con il nostro modello DateRange ed appena incollato il codice in un nuovo 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!

Ora quando si esegue la soluzione si dovrebbe vedere una scatola rossa attorno al nostro DateRange. Il resto delle personalizzazioni dipende da te! È possibile aggiungere caselle jQuery DatePicker. Nel tuo caso potresti mettere entrambi i campi in un singolo div in modo che si allineano in orizzontale. Il cielo è il limite a questo punto.

Fase 3: Convalida:

convalida funziona praticamente solo il modo in cui ci si aspetterebbe. Un attributo [obbligatorio] all'interno del tuo tipo DateRange funziona esattamente come qualsiasi altro attributo di convalida.

Ora si vede che non posso tornare indietro nell'attributo time che ho inserito nella proprietà DateRange di AppointmentViewModel. Tutto quello che devi fare per creare questo tipo di convalida attributi specifici è ereditare e implementare la ValidationAttribute di 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; 
    } 
} 

Ora, se si aggiunge questo e decorare la vostra proprietà si dovrebbe vedere il messaggio di errore previsto l'attributo di convalida CantGoBackwardsInTime personalizzato.

Aggiornerò e chiarirò di più se avete qualche problema, ma questo dovrebbe iniziare e arrivare. (pensavo di poterlo scoprire prima di andare a letto) Solo un avvertimento: il nuovo Editor per i pezzi di MVC 2 è la cosa più fantastica del mondo e ha un enorme potenziale per offrire le eccellenti capacità RAD di MVC 2; eppure c'è poco da sapere oltre al blog di Brad Wilsons. Continua a farlo e non aver paura di dare un'occhiata al codice sorgente di MVC 2 se ne hai bisogno.

+0

Grazie per questo jfar, mi ha sicuramente dato qualcosa su cui lavorare, ma sono più propenso a dividere l'oggetto datetime nei suoi input component (un input separato per anno, mese e giorno, ad esempio) piuttosto che più datetime completi le istanze. Pensi che questa sia ancora la strada giusta da fare? Come ti piacerebbe aggiungere la validazione lato client come parte di questa soluzione? –

+0

Bene, giusto. Dovrai semplicemente creare il tuo modello DateTime che dividerà l'interfaccia utente. Per quanto riguarda la validazione lato client, è sufficiente includere il javascript nel modello DateTime.ascx personalizzato. – jfar

+0

Ok, ho lavorato con una delle combinazioni che stavo cercando (mese e anno solo in due input separati) quindi un enorme ringraziamento per il tuo aiuto e lo contrassegnerò come risposta. Una cosa che mi sto chiedendo ora è che io abbia una validationattribute personalizzata che prende un oggetto CardDate (int mese e int anno) e prova a formare una data valida. se fallisce, restituisce correttamente false affermando che il carddate non è valido. Una cosa è possibile all'interno dell'attributo per rendere entrambi i campi all'interno dell'oggetto cardtype non validi, in modo che vengano visualizzati in uno stato non valido sulla vista? Spero che abbia senso! –

0

Stai cercando di fare tutto questo in una sola pagina? O un metodo? Non sono sicuro di come lo vuoi fare, quindi ecco il mio scatto se ti capisco bene.

Così, ad esempio, si potrebbe fare qualcosa del genere per la data di nascita.

Secondo lei

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

Nel vostro controller avere da qualche parte qualcosa di simile. Forse potresti combinarlo tutto in un unico ciclo o qualcosa del genere.

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"]. 

Così che cosa voglio arrivare è il vostro grado di fare alcune cose sul server per ottenere le date che si desidera e basta aggiungere attraverso Viewdata in elenchi a discesa.

+0

Ciao chobo2, grazie per l'input, ma questo non sta andando nella giusta direzione. Sto cercando un modo per associare una proprietà datetime su un modello e continuare a convalidarlo, ecc. Il collegamento nel commento sopra dovrebbe dare un'idea migliore di ciò che sto cercando di realizzare. –

Problemi correlati