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

5

7

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?

Harriette answered 14/7, 2011 at 12:28 Comment(0)
M
3

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.

Mencher answered 14/7, 2011 at 13:45 Comment(0)
M
3

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]
Mcgraw answered 14/7, 2011 at 13:0 Comment(0)
M
3

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.

Mencher answered 14/7, 2011 at 13:45 Comment(0)
L
1

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.

Looby answered 14/7, 2011 at 13:0 Comment(0)
C
0

Probably not the most efficient, but in your viewmodel you could have three ints for the day, month, and year respectively. Then when you return the submitted viewmodel you can simply use the three fields to build a DateTime object.

As far as creating a template, I think a partial view would be the best way to go. Not sure, I'm still learning MVC myself.

Cella answered 14/7, 2011 at 12:54 Comment(0)
C
0

if you are using html.editorfor you can specify your own editors by adding an ascx file in views/shared/Editors which defines the control, and the adding of the three fields would be done in the code behind of that file.

Chamkis answered 14/7, 2011 at 13:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.