Unable to bind a datetime field to a part in the admin editor

Topics: Customizing Orchard, Writing modules
Jan 20, 2013 at 8:41 AM

I'm creating a simple countdown widget, which has a single field which is a datettime, here is my part's mode:

 

    public class CountdownRecord : ContentPartRecord
    {
        public virtual DateTime ReleaseDate { get; set; }
    }

    public class CountdownPart : ContentPart<CountdownRecord>
    {
        [Required]
        public DateTime ReleaseDate
        {
            get { return Record.ReleaseDate; }
            set { Record.ReleaseDate = value; }
        }
    }

And the migration:

    public class Migrations : DataMigrationImpl
    {
        public int Create()
        {
            SchemaBuilder.CreateTable("CountdownRecord", table => table
                .ContentPartRecord().Column("ReleaseDate", DbType.DateTime)
            );

            ContentDefinitionManager.AlterPartDefinition(
                typeof(CountdownPart).Name, cfg => cfg.Attachable());

            return 1;
        }

        public int UpdateFrom1()
        {
            // Create a new widget content type with our countdown part
            ContentDefinitionManager.AlterTypeDefinition("CountdownWidget", cfg => cfg
                .WithPart("CountdownPart")
                .WithPart("WidgetPart")
                .WithPart("CommonPart")
                .WithSetting("Stereotype", "Widget"));

            return 2;
        }
    }

The driver:

 

    public class CountdownDriver : ContentPartDriver<CountdownPart>
    {
        protected override DriverResult Display(CountdownPart part, string displayType, dynamic shapeHelper)
        {
            return ContentShape("Parts_Countdown", () => shapeHelper.Parts_Countdown(ReleaseDate: part.ReleaseDate));
        }
        //GET
        protected override DriverResult Editor(CountdownPart part, dynamic shapeHelper)
        {
            return ContentShape("Parts_Countdown_Edit",() => shapeHelper.EditorTemplate(TemplateName: "Parts/Countdown",Model: part,Prefix: Prefix));
        }
        //POST
        protected override DriverResult Editor(CountdownPart part, IUpdateModel updater, dynamic shapeHelper)
        {
            updater.TryUpdateModel(part, Prefix, null, null);
            return Editor(part, shapeHelper);
        }
    }

And the admin view (editor template):

@model Countdown.Models.CountdownPart
@{
    Script.Require("jQuery");
    Script.Require("jQueryUI");
    Style.Require("jQueryUI_Styles");
 }

<fieldset>
    <legend>Countdown Fields</legend>

    
class="editor-label"> @Html.LabelFor(x => x.ReleaseDate)
class="editor-field"> @Html.TextBoxFor(x => x.ReleaseDate, new{id="coundownDatePicker"}) @Html.ValidationMessageFor(x => x.ReleaseDate)
</fieldset> @using (Script.Foot()) { <script type="text/javascript"> $("#coundownDatePicker").datepicker(); </script> }

The problem is when I put a breakpoint to examine the part that is submitted as the post action in the driver, the part's datetime property (ReleaseDate) is always null (actually its just 01/01/0001) and the widget is not persisted to the database. (Obviouslt I've defined a handler, I've followed the orchard docs tutorial on creating a widget).

 

Any ideas? Would be grateful for any guidance.

 

Thanks.

Developer
Jan 20, 2013 at 9:12 AM

What you can try is as soon as the breakpoint hits, move up on the stack to where the _contentManager.UpdateEditor is called (it's in the AdminController of the Contents module). From there, you will be able to inspect the HttpContext and its Request.Form collection. Inspect that collection of form values and find out what the exact key of the datetime field is and its value. If the key/value pair is OK, then at least you know that is not the problem. However make sure that the modelbinder works with this key/value pair by carefully inspecting the properties on your part after the call to TryUpdateModel, perhaps by using a temporary mock class with a simple DateTime automatic property.

Jan 20, 2013 at 9:45 AM

Thanks smf. I examined the stack trace and the key-value pairs of the forms binding, the ReleaseDate property's values was set correctly in the form collection: [System.Web.HttpValueCollection] =Attempted Value:"02/28/2014" and in the huge generated serialised form string I found '....ReleaseDate=02%2f28%2f2014.....', so it looks as though thats not the problem. It's funny that I've set the date on a textbox using the jqueryUI datepicker hundreds of times in my other MVC projects, but it seems to be failing here. I've never had problems with string/int input fields either.

Developer
Jan 20, 2013 at 10:06 AM

Ok, next step would be to verify that that string can actually be parsed by the model binder using a simple viewmodel (which has a ReleaseDate property). Start with the type being a DateTime. If that fails, try with the string type. If that works, it's probably a culture mismatch. I don't know which Parse method the modelbinder is using, but it's probably something like DateTime.ParseExact.