Bootstrap / KO:具有特定日期/时间的日期/时间选择器

时间:2014-12-31 19:06:52

标签: javascript twitter-bootstrap knockout.js datepicker

我正在尝试获得以下功能:

我有一个SPA,一旦加载,访问REST API并接收日期+时间值列表。

我的目标是让用户轻松选择日期和时间:

  1. 如果特定日期只有一个日期+时间值 - 默认选择它。
  2. 如果特定日期的值不止一个 - 允许用户通过另一个显示日期的弹出框选择其中一个。
  3. 我开始尝试使用Bootstrap datetimepicker V3。我定义了一个ko.bindingHandlers(网上有很多例子),但问题是当绑定发生并启动日期时间选择器时,可观察的有效日期数组仍为空(稍后加载)。我没有找到更新小部件的方法(我尝试注册数组更改并重新初始化datetimepicker:

    $el.datetimepicker('destroy'); // or 'remove'
    $el.datetimepicker(options);   // after I updated options with the new enabledDates
    

    接下来我想和bootstrap-datepicker一起试试运气。在这里,使用绑定和'beforeShowDay'选项,我管理以获得正确的日期。在init函数中我有:

    if (allBindingsAccessor().enabledDates) {
        var enabledDates = allBindingsAccessor().enabledDates;
        options["beforeShowDay"] = function (date) {
            for (var i = 0; i < enabledDates().length; i++) {
                var innerDate = enabledDates()[i];
                var datePart = innerDate.dateOnly();
                if (datePart.getTime() == date.getTime()) {
                    return true;
                }
            }
            return false;
         }
    } 
    

    然而,当选择日期时 - 我尝试做同样的逻辑,将整个日期“重新翻译”到确切的日期+时间,但运作不正常(见下面的代码)。

    最理想的是,我想在日期上显示一些弹出窗口,以防有多个可用时间。否则,即使是两步选择(选择日期,然后从组合中选择时间)也可以。

    我创建了一个js-fiddle,它显示了我设法做的事情example fiddle

    我可以使用一些现成的组件吗?有没有人知道如何实施?

    以下代码无法正常工作 - 如果我使用setDate()函数,则无法在有界可观察对象中获得任何内容。

    ko.bindingHandlers.datepicker = {
        init: function (element, valueAccessor, allBindingsAccessor) {
    
            //initialize datetimepicker with some optional options
            var options = allBindingsAccessor().datepickerOptions || {},
                $el = $(element);
    
            // in case enabledDates attribute exists, add a function to check if date exists
            // this is used later to set the exact date+time
            var getDateTime = function() {
                if (allBindingsAccessor().enabledDates) {
                    // TODO: make more efficient
                    var enabledDates = allBindingsAccessor().enabledDates;
    
                        for (var i = 0; i < enabledDates().length; i++) {
                            var td= enabledDates()[i];
                            var datePart = new Date(td.getFullYear(), td.getMonth(), td.getDate());
                            if (datePart.getTime() == date.getTime()) {
                                return innerDate;
                            }
                        }
                        return null;
                }
            };
    
            if (allBindingsAccessor().enabledDates) {
                var enabledDates = allBindingsAccessor().enabledDates;
                options["beforeShowDay"] = function (date) {
                    for (var i = 0; i < enabledDates().length; i++) {
                        var td = enabledDates()[i];
                        var datePart = new Date(td.getFullYear(), td.getMonth(), td.getDate());
                        if (datePart.getTime() == date.getTime()) {
                            return true;
                        }
                    }
                    return false;
                }
            }
    
            $el.datepicker(options);
    
            // register to change event and update the value accessor (property) on change
            function setDate(date) {
                if (!date) {
                    return date;
                }
    
                if (allBindingsAccessor().enabledDates) {
                    var enabledDates = allBindingsAccessor().enabledDates;
                    for (var i = 0; i < enabledDates().length; i++) {
                        var td = enabledDates()[i];
                        var datePart = new Date(td.getFullYear(), td.getMonth(), td.getDate());
                        if (datePart.getTime() == date.getTime()) {
                            return innerDate;
                        }
                    }
                } else {
                    return date;
                }
            };
    
            ko.utils.registerEventHandler(element, "changeDate", function(event) {
                var value = valueAccessor();
                if (ko.isObservable(value)) {
                    var actualDate = setDate(event.date);
                    //console.log(actualDate);
                    value(actualDate);
                }
            });
    
            //handle disposal (if KO removes by the template binding)
            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                $el.datetimepicker("destroy");
            });
        },
        update: function (element, valueAccessor) {
            var value = ko.utils.unwrapObservable(valueAccessor()),
                $el = $(element);
    
            var current = $el.data("datepicker").getDate();
    
            //var datePart = new Date(value.getFullYear(), value.getMonth(), value.getDate());
            if (value - current !== 0) {
                $el.data("datepicker").setDate(value);
            }
        }
    };
    

1 个答案:

答案 0 :(得分:0)

我做了一些&#34;解决方法&#34;其中我将选择的日期和时间分成两个不同的元素。

Here是一个有效的例子。通常,流程定义为:

  • 用户从可用日期中选择日期(由日期选择器中的beforeShowDay函数处理+在绑定(enabledDates选项)中传递的可观察数组。
  • 日期的选择更新了绑定的observable(selectedDate)。在模型中,我订阅了observable的更改事件。
  • selectedDate observable更改时,我找到与所选日期对应的所有日期+时间值,并填充选择的选择器绑定到的可观察数组(avaiabledTimes
  • 然后,用户可以选择绑定到observable(selectedTime)的时间。在模型中,我订阅了它的更改事件。
  • selectedTime可观察到的变化时,我&#34;合并&#34;所选日期和所选时间,并设置最终选定日期(finalSelectedDate)。

虽然这种作品,但它不是我想要的100%:

  1. 用户流程并不像我想的那么顺畅。
  2. 当我设置availabledTimes时,由于selectpicker的行为,默认情况下会选择第一个元素,这不是我想要的,因为每当finalSelectedDate更改时我会调用一些更新UI的ajax例程 - 这意味着在当前设置中,我可能会收到不需要的更新。 (我认为它可能在绑定中 - 我也会尝试修复它。
  3. 从代码的角度来看 - 没有封装 - 我需要弄清楚如何将它作为一个组件进行管理(例如ko.components.register('MyDateTimePicker', ...)