Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 310
BrunoLM Avatar asked Jul 14 '11 12:07

BrunoLM


3 Answers

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.

like image 96
James McCormack Avatar answered Oct 13 '22 16:10

James McCormack


Scott Hanselman has a blog post on creating a custom model binder to handle DateTimes. It doesn't exactly fit your scenario, but it should give you some ideas, and once that's in place the editor template should be much easier...

In terms of where to place the file once it's created - that's the easy bit:

~/Views/Shared/EditorTemplates/DateTime.[ascx|cshtml|vbhtml]
like image 21
Jon Avatar answered Oct 13 '22 16:10

Jon


Editor Templates are your best bet. If you want the editor template available from anywhere, put it in the Views/Shared/EditorTemplates folder. If you want all DateTime types to use this template, then create a partial called DateTime. If you only want some of them to use this template, then call it something else, and use the UIHintAttribute attribute and create an editor template with the same name as the value you use for that attribute.

To have the model binder continue working, you may need to add some javascript to your editor, and onchange for any of the drop dropdowns should update a hidden field (with the correct name so the model binder will work) with the selected month/day/year values.

like image 1
Brian Ball Avatar answered Oct 13 '22 15:10

Brian Ball