jwaliszko / ExpressiveAnnotations

Annotation-based conditional validation library.
MIT License
351 stars 123 forks source link

Support DateTimeOffset type #128

Closed Korayem closed 8 years ago

Korayem commented 8 years ago

Currently when I do checks like [AssertThat(" StartDate.UtcDateTime < EndDate.UtcDateTime", ErrorMessage = "Start Date is after End Date")] it fails on client side image

Same error in case I use EndDate.LocalDateTime

jwaliszko commented 8 years ago

There are two issues here:

Solution can be as follows:

Korayem commented 8 years ago

I am sorry I didn't share the underlyings of my code

All my DateTimeOffset properties use DisplayTemplate:

@model DateTimeOffset?

<span class="datetime">
    @(Model != null ? string.Format(ViewData.ModelMetadata.DisplayFormatString ?? "{0:dd/MM/yyyy hh:mm tt}", Model.Value.UtcDateTime, System.Globalization.CultureInfo.InvariantCulture) : string.Empty)
</span>

and EditorTemplate:

@model DateTimeOffset?
@if (Model != null)
{
    @Html.TextBox("", Model.Value.LocalDateTime.ToString("dd/MM/yyyy hh:mm tt", System.Globalization.CultureInfo.InvariantCulture), new { @class = "form-control" })
}
else
{
    @Html.TextBox("", "")
}

I will try to incorporate your solution into my Templates and Entities and see how it works

Note also that I am using EA directly on my entities without DTOs

jwaliszko commented 8 years ago

Regarding the second issue I've mentioned - since this the commit https://github.com/jwaliszko/ExpressiveAnnotations/commit/96b5caa2d17b5d1bf1cf81a1054950332a65383d, now all the overloaded operators are executed.

Another change - at the client I've just enabled, see https://github.com/jwaliszko/ExpressiveAnnotations/commit/fe913b3094f27665e92449ae91afb248d594cffa, recognition of any ValueParser attributes applied to nested properties. Due to that there is an alternative solution to that one I've provided above:

jwaliszko commented 8 years ago

One more comment regarding your HTML. Due to the fact you're not using the HTML helpers, HTML is incorrect. EA cannot identify certain fields, and the same the script cannot create a proper model (for the expression to be evaluated against).

If you're creating the HTML without the helpers you should provide input fields with correct names - property paths - which appear in the expressions. In this particular example it could look like this:

<input name="StartDate.UtcDateTime" value="@Model.StartDate.UtcDateTime.ToString("dd/MM/yyyy")">
<input name="EndDate.UtcDateTime" value="@Model.EndDate.UtcDateTime.ToString("dd/MM/yyyy")">

Usage of custom HTML has big advantage here though - you can serialize the date offset to a string which represents RFC 2822 or ISO 8601 format, acceptable by JS date.Parse() (see the line pointed above as fragile, which is not fragile anymore when proper date format is used).

Korayem commented 8 years ago

Thanks @jwaliszko your answers put me in the right directions. But I took a different approach. Here's what I did

In my Entity:

[AssertThat("DateTimeOffsetInFuture(ScheduledDateTime)")]
public DateTimeOffset ScheduledDateTime { get; set; } = DateTimeOffset.UtcNow;

public bool DateTimeOffsetInFuture(DateTimeOffset scheduledDateTime)
{
     return DateTimeOffset.UtcNow.LocalDateTime < scheduledDateTime.LocalDateTime;
}

In my ea.methods.js I added custom parser that parses all properties of type DateTimeOffset and implemented DateTimeOffsetInFuture() using moment.js library

$(function () {
    ea.addMethod('DateTimeOffsetInFuture', function (scheduledDateTime) {
        var dateselected = moment(scheduledDateTime);
        return Date.now() < dateselected;
    });
    //Properly parse DateTimeOffset
    ea.addValueParser('datetimeoffset', function (value, field) {
        return moment(value, 'DD/MM/YYYY hh:mm a');
    });
});