使用日期时间选择器进行日期和时间验证 (ASP.Net MVC Core v1.1)

Posted

技术标签:

【中文标题】使用日期时间选择器进行日期和时间验证 (ASP.Net MVC Core v1.1)【英文标题】:Date and time validation with date time picker (ASP.Net MVC Core v1.1) 【发布时间】:2018-01-15 08:20:55 【问题描述】:

我正在尝试使用弹出模式在日历中创建事件,但无法让 jQuery 验证在事件的开始时间和结束时间字段上工作。 我不知道如何相互验证这两个字段,以便结束时间在开始时间之前无效。

它似乎在表单第一次弹出时显示的默认日期部分起作用,但如果我更改开始日期,它就会停止工作。另一方面,尝试验证日期的时间部分从未奏效。

查看模型:

public class EventViewModel

    public int Id  get; set; 

    public string Title  get; set; 

    [Required]
    [DataType(DataType.DateTime)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "0:yyyy-MM-dd HH:mm")]
    public DateTime Start  get; set; 

    [Required]
    [DataType(DataType.DateTime)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "0:yyyy-MM-dd HH:mm")]
    [DateGreaterThan("Start", ErrorMessage = "End date has to be later than start date")]
    public DateTime End  get; set; 

    public string Color  get; set; 

    public bool AllDay  get; set; 

    [Required]
    public string StudentId  get; set; 

    [Required]
    public int LessonTypeId  get; set; 
  

控制器:

public async Task<IActionResult> Index()
    
        EventViewModel Event = new EventViewModel  Start = DateTime.Now, End = DateTime.Now.AddMinutes(30) ;
        var user = await GetCurrentUserAsync();
        var studentList = await GetTeacherStudentsAsync(user);
        ViewData["StudentList"] = new SelectList(studentList, "Id", "FirstName");
        var lessonTypeList = _context.LessonTypes.Where(l => l.TeacherId.Equals(user.Id));
        ViewData["LessonTypeList"] = new SelectList(lessonTypeList, "LessonTypeId", "Description");
        return View(Event);
    

索引视图:

<head>
<script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/css/tether.min.css" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.css" />
  <script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/gcal.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.4/jquery.datetimepicker.css" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.4/build/jquery.datetimepicker.full.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
  <script type="text/javascript">
    $(document).ready(function() 

      $('#calendar').fullCalendar(
        customButtons: 
          createButton: 
            text: "new event",
            click: function() 
              $('#createModal').modal('show');
            
          
        ,
        header: 
          left: 'prev,next today createButton',
          center: 'title',
          right: 'month,agendaWeek,agendaDay,listWeek'
        ,
        defaultView: "month",
        allDaySlot: false,
        eventLimit: true,
        editable: true,
        navLinks: true,
        events: "/Calendar/GetEvents",
        eventDrop: function(event, delta, revertFunc) 

          alert(event.title + " was dropped on " + event.start.format());

          if (confirm("Are you sure you want to make this change?")) 
            SaveEvent(event);
           else 
            revertFunc();
          

        ,
        eventResize: function(event, delta, revertFunc) 

          alert(event.title + " is now from " + event.start.format() + " to " + event.end.format());

          if (confirm("Are you sure you want to make this change?")) 
            SaveEvent(event);
           else 
            revertFunc();
          

        
      );

      $.validator.addMethod("laterThan", function(value, element, params) 
        var start = params.split(" ");
        var startDate = new Date(start[0]);
        var startTime = start[1].split(":");
        var end = value.split(" ");
        var endDate = new Date(end[0]);
        var endTime = end[1].split(":");
        if (startDate == endDate) 
          if (parseInt(startTime[0], 10) == parseInt(endTime[0], 10)) 
            return parseInt(startTime[1], 10) > parseInt(endTime[1], 10);
           else if (parseInt(startTime[0], 10) < parseInt(endTime[0], 10)) return true;
          else return false;
        
        return this.optional(element) || startDate < endDate;
      , "End time must be later than start time");

      var validator = $("#createForm").validate(
        rules: 
          Start: "required",
          End: 
            required: true,
            laterThan: $("#Start").val(),
          
        
      );

      $(function() 
        $("#Start").datetimepicker(
          format: "Y-m-d H:i",
          onChangeDateTime: function(ct) 
            $(this).valid();
          
        );
        $("#End").datetimepicker(
          format: "Y-m-d H:i",
          onShow: function(ct) 
            var start = $("#Start").val().split(" ");
            this.setOptions(
              minDate: start[0]
            );
          ,
          onChangeDateTime: function(ct) 
            $(this).valid();
          
        );
      );
    );


    function SaveEvent(Event) 

      var dataRow = 
        Id: Event.id,
        Start: Event.start,
        End: Event.end
      

      $.ajax(
        method: 'POST',
        url: '@Url.Action("SaveEvent", "Calendar")',
        dataType: "json",
        contentType: "application/json",
        data: JSON.stringify(dataRow),
        error: function(result) 
          alert("Something went wrong... Event Id was: " + Event.id + ", Start Time was: " + Event.start.format());
        
      );
    

    function CreateEvent() 

      var valid = validator.form();
    

    function DeleteEvent(Event) 

      var dataRow = 
        Id: Event.id
      

      $.ajax(
        method: 'POST',
        url: '@Url.Action("DeleteEvent", "Calendar")',
        dataType: "json",
        contentType: "application/json",
        data: JSON.stringify(dataRow),
        error: function(result) 
          alert("Something went wrong... Event Id was: " + Event.id)
        
      )

    
  </script>
</head>

<body>
  <div id='calendar'></div>

  <div class="modal fade" id="createModal" tabindex="-1" role="dialog" aria-labelledby="createModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h4 class="modal-title" id="createModalLabel">Create New Event</h4>
          <button type="button" class="close" data-dismiss="modal">×</button>
        </div>
        <div class="modal-body">
          <form id="createForm">
            <div class="form-group">
              <label for="StudentId" class="col-md-2 form-control-label">Student</label>
              <div class="col-md-10">
                <select asp-for="StudentId" asp-items="ViewBag.StudentList" class="form-control"></select>
              </div>
            </div>
            <div class="form-group">
              <label for="LessonTypeId" class="col-md-3 form-control-label">Lesson Type</label>
              <div class="col-md-9">
                <select asp-for="LessonTypeId" asp-items="ViewBag.LessonTypeList" class="form-control"></select>
              </div>
            </div>
            <div class="form-group">
              <label for="Start" class="col-md-2 form-control-label">Start</label>
              <div class="col-md-10">
                <input asp-for="Start" class="form-control" id="Start" name="Start" />
              </div>
            </div>
            <div class="form-group">
              <label for="End" class="col-md-2 form-control-label">End</label>
              <div class="col-md-10">
                <input asp-for="End" class="form-control" id="End" name="End" />
              </div>
            </div>
          </form>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-primary" id="createButton" onclick="CreateEvent()">Create</button>
          <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
        </div>
      </div>
    </div>
  </div>
</body>

任何建议将不胜感激!

编辑(2017 年 8 月 20 日)- 我尝试使用 Microsoft 的指南合并自定义验证,但这似乎也不起作用(一个数据属性也添加到视图模型中)。

验证类:

public class DateGreaterThan : ValidationAttribute, IClientModelValidator

    private readonly string _earlierDate;

    public DateGreaterThan (string earlierDate)
    
        _earlierDate = earlierDate;
    

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    
        ErrorMessage = ErrorMessageString;

        var lateDate = (DateTime)value;
        var otherProperty = validationContext.ObjectType.GetProperty(_earlierDate);
        if (otherProperty == null)
            throw new ArgumentException("Property with this name could not be found");

        var earlyDate = (DateTime)otherProperty.GetValue(validationContext.ObjectInstance);

        if (lateDate <= earlyDate)
            return new ValidationResult(ErrorMessage);

        return ValidationResult.Success;
    

    public void AddValidation(ClientModelValidationContext context)
    
        var error = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
        context.Attributes.Add("data-val", "true");
        context.Attributes.Add("data-val-error", error);
    

【问题讨论】:

【参考方案1】:

因此,在对不同的选项进行了几周的测试并对此感到头疼之后,我意识到我一直在尝试实施的验证存在错误的逻辑,原因如下:

    由于我构建自定义 jQuery 验证的方式,被比较的开始日期的值只传递给验证一次 - 当页面加载时。因此,当用户更改起始字段的输入时,它并没有改变(因此它保持为默认值或空值)。 我完全忽略了日期对象也存储时间的面孔——因为我试图手动进行时间比较。

在那之后只花了几分钟的时间来解决这些问题:

    不是将参数传递给自定义验证,而是使用 jQuery 直接从表单中为其提供开始日期。 摆脱所有手动小时比较代码并进行简单的日期比较。

现在自定义验证代码被缩减为简单的 3 行:

        $.validator.addMethod("laterThan", function (value, element) 
            var startDate = new Date($("#Start").val());
            var endDate = new Date(value);

            return this.optional(element) || startDate < endDate;
        , "End time must be later than start time");  

感谢您的建议!

【讨论】:

【参考方案2】:

对于您的案例,我认为最好的选择是“MVC 万无一失的验证”。您可以为您的案例使用“GreaterThan”或“LessThan”数据注释。因此,您可以在“结束”属性的注释中包含类似的内容:

[GreaterThan("Start", ErrorMessage = "Expiry Date must be greater than Activation Date", PassOnNull = true)]

查看this 了解有关 Foolproof 的更多信息。

【讨论】:

非常感谢您的建议!我一定会试一试的。有没有类似的东西可以即时进行客户端验证?由于它是一个弹出模式,如果在用户将数据输入字段而不是提交之后弹出验证错误可能会更好。 这会在客户端处理验证。 我尝试安装 Foolproof Validation 却发现遗憾的是它不适用于 MVC Core 1.1 版。你知道有没有更新但类似的插件?我能找到的所有内容都与 MVC Core 完全不兼容。我还尝试使用自定义验证属性来比较这两个属性,但这似乎也不起作用。 很抱歉花了这么长时间才回复 - 我有一段时间没有足够的时间坐下来研究这个程序。

以上是关于使用日期时间选择器进行日期和时间验证 (ASP.Net MVC Core v1.1)的主要内容,如果未能解决你的问题,请参考以下文章

引导验证器和日期时间选择器冲突

.NET Web 表单下拉列表和日期时间选择器需要 HTML 5 验证

你如何让剑道日期选择器对最短日期进行日期验证?

JavaScript:日期选择器组件的使用

JavaScript:日期选择器组件的使用

如何使用反应表单挂钩验证反应日期选择器