DataForm not working with DateOnly fields (unless they're nullable?)

Hey there.

I have a data form nested in a dialog.  The DTO I've created has three fields: a string, a DateOnly, and a nullable DateOnly.  When the dialog is shown, the DTO is loaded with values from its parent.  The trouble is that the form does not display the "Install Date" value unless I define it as nullable in the DTO. This value is never null, and is definitely assigned to the DTO along with the "Removal Date" and "Address" fields.  The values for the other fields appear correctly.  



Screenshot 2024-12-17 162948 (2).jpg  


The additional relevant code appears below:

                    <SfDataForm ID="EditForm" Model="@mplEdit" OnValidSubmit="OnValidSubmitHandler">
                        <FormValidator>
                            <DataAnnotationsValidator></DataAnnotationsValidator>
                        </FormValidator>
                        <FormItems>
                            <FormItem Field="@nameof(mplEdit.StreetAddress)"></FormItem>
                            <FormItem Field="@nameof(mplEdit.InstallDate)"></FormItem>
                            <FormItem Field="@nameof(mplEdit.RemovalDate)"></FormItem>
                        </FormItems>
                        <FormButtons>
                            <SfButton IsPrimary="true" typeof="submit">Save</SfButton>
                            <SfButton OnClick="OnCloseDialog">Close</SfButton>
                        </FormButtons>
                    </SfDataForm>
@code {
    [CascadingParameter]
    public mplLinks Parent { get; set; } = default!;
    [Parameter]
    public mpl? SelectedLink { get; set; } = default!;
    [Parameter]
    public string CardHeaderTitle { get; set; } = "";
    public bool DialogVisible { get; set; } = false;
    SfDialog mplEditDialog = default!;
    mplDTO mplEdit = new();


    public async Task ConfirmMarkDeleted()
    {
        bool proceed = await DialogService.ConfirmAsync("This function will mark this link as deleted.");
        if (proceed)
        {
            await Parent.MarkLinkDeleted(SelectedLink);
        }
    }


    public void EditLink()
    {
        DateOnly? removalDate = SelectedLink.RemovalDate != null ? DateOnly.FromDateTime((DateTime)SelectedLink.RemovalDate) : null;
        mplEdit = new mplDTO
            {
                InstallDate = DateOnly.FromDateTime(SelectedLink.InstallDate),
                RemovalDate = removalDate,
                StreetAddress = SelectedLink.Parcel.AddressLine1
            };
        DialogVisible = true;
    }


    public async Task OnValidSubmitHandler()
    {
        await Parent.UpdateMeterParcelLink(SelectedLink, mplEdit);
//OnCloseDialog();
    }

    public void OnCloseDialog()
    {
        mplEdit = new();
        DialogVisible = false;
    }

    public class mplDTO
    {
        [DTOValidation]
        [Display(Name = "Install Date")]
        [Required]
        public DateOnly InstallDate { get; set; }
        [DTOValidation]
        [Display(Name = "Removal Date")]
        public DateOnly? RemovalDate { get; set; }
        [Editable(false)]
        [Display(Name = "Street Address")]
        public string StreetAddress { get; set; } = default!;
    }


    public class DTOValidationAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            mplDTO link = (mplDTO)validationContext.ObjectInstance;


            DateTime? compare = value as DateTime?;


            if (link.RemovalDate != null && link.RemovalDate < link.InstallDate)
            {
                return new ValidationResult("The removal date cannot be before the install date.", new[] { validationContext.MemberName });
            }


            if (link.RemovalDate != null && link.RemovalDate == link.InstallDate)
            {
                return new ValidationResult("The installation date is probably not the same as the removal date. That's a busy meter.", new[] { validationContext.MemberName });
            }
            return ValidationResult.Success;
        }
    }
}

Is there something I'm missing? If I redefine the InstallDate field as nullable, the value shows up, but then I have to manage the possible null value when I'm transferring the DTO values back to the "real" object, and it seems to me that that's more trouble than it should be.  Any help would be appreciated.

Thanks,

Christopher

Edit: for the record, this is on the most recent version - 28.1.33.  It was also happening in version 27.2.5.


2 Replies

UD UdhayaKumar Duraisamy Syncfusion Team December 20, 2024 11:04 AM UTC

Hello Christopher,

We have considered the reported scenario "The DatePicker is not rendered for a non-nullable DateOnly type" as a bug from our end. The fix for this issue is scheduled to be included in the patch release planned for mid-January 2025.
 
You can track the status of this issue through the following link:
 
Disclaimer: “Inclusion of this solution in the weekly release may change due to other factors including but not limited to QA checks and works reprioritization.”

Regards,

Udhaya Kumar D.



UD UdhayaKumar Duraisamy Syncfusion Team January 7, 2025 01:03 PM UTC

We’re pleased to inform you that the issue, "The DatePicker is not rendered for a non-nullable DateOnly type" has been resolved in our release version 28.1.38. We recommend upgrading to latest version to address the issue.
 

Root Cause:
The rendering of the component was not handled for non-nullable DateOnly types, causing it to be skipped when the respective type was used, and the components were auto-generated.
 
Solution:
We have now added a condition and a switch case to ensure the component is properly rendered when non-nullable DateOnly and TimeOnly types are used.

Loader.
Up arrow icon