Search code examples
c#asp.net-mvcasp.net-mvc-3editortemplates

How to create a editor template for DateTime with 3 fields?


I want to create an editor template for DateTime, I need 3 separated fields:

(DropDown) Day    |    (DropDown) Month    |    (DropDown) Year

How and where do I create this file? And what do I need to do to turn these 3 fields into a single DateTime when I post to a controller?


Solution

  • In your Views/Shared/EditorTemplates folder create a partial view called DateTime.ascx.

    The code for this EditorTemplate should be something like

    <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %>
    
    <%
        string controlId = ViewData.TemplateInfo.HtmlFieldPrefix.Replace('.', '_');
    %>
    
    <script type="text/javascript">
    $(function () {
        $('#<%: controlId %>_Day, #<%: controlId %>_Month, #<%: controlId %>_Year').live('change', function () { updateHiddenDate('<%: controlId %>'); });
        $('#<%: controlId %>_Day').val('<%: Model.HasValue ? Model.Value.Day.ToString() : "" %>');
        $('#<%: controlId %>_Month').val('<%: Model.HasValue ? Model.Value.Month.ToString() : "" %>');
        $('#<%: controlId %>_Year').val('<%: Model.HasValue ? Model.Value.Year.ToString() : "" %>');
        updateHiddenDate('<%: controlId %>');
    });
    
    function updateHiddenDate(hiddenDateId) {
        $('#' + hiddenDateId).val($('#' + hiddenDateId + '_Year').val() + "-" + $('#' + hiddenDateId + '_Month').val() + "-" + $('#' + hiddenDateId + '_Day').val());
    }
    </script>
    
    <select id="<%: controlId %>_Day">
    <%  for (int dayOrdinal = 1; dayOrdinal <= 31; dayOrdinal++)
        {
            Response.Write(string.Format("<option value=\"{0}\">{0}</option>", dayOrdinal));
        }
    %>
    </select>
    <select id="<%: controlId %>_Month">
    <%  for (int monthOrdinal = 1; monthOrdinal <= 12; monthOrdinal++)
        {
            Response.Write(string.Format("<option value=\"{0}\">{1}</option>", monthOrdinal, System.Globalization.DateTimeFormatInfo.CurrentInfo.MonthNames[monthOrdinal - 1]));
        }
    %>
    </select>
    <select id="<%: controlId %>_Year">
    <%  for (int yearOrdinal = DateTime.Now.Year - 5; yearOrdinal <= DateTime.Now.Year + 5; yearOrdinal++)
        {
            Response.Write(string.Format("<option value=\"{0}\">{0}</option>", yearOrdinal));
        }
    %>
    </select>
    
    <%: Html.Hidden("", Model.HasValue ? String.Format("{0:yyyy-MM-dd}", Model) : "") %>
    

    That creates an editor template with a hidden field containing an ISO 8601 representation of the date which the MVC ModelBinder can parse.

    The jQuery updates the hidden field whenever the dropdowns change. Note the use of the ViewData.TemplateInfo.HtmlFieldPrefix that I use to get the generated id of the hidden field.

    Note that this solution drops in easily without faffing about with Custom ModelBinders because we construct a single form value containing the full datetime. However, this does mean that

    1. You rely on the client having javascript enabled, and
    2. You need to include a script reference to the jQuery library in your masterpage (e.g. <script type="text/javascript" src="../../Scripts/jquery-1.4.1.min.js"></script>)

    If that's not acceptable, you will have to look at Custom ModelBinders as @Jon has pointed to.