【问题标题】:Using Knockout.js how do bind a Date property to a HTML5 date picker?使用 Knockout.js 如何将 Date 属性绑定到 HTML5 日期选择器?
【发布时间】:2013-08-03 17:07:50
【问题描述】:

(目前这只适用于 Chrome,因为大多数浏览器还没有为 input type="date" 实现日期选择器)

在以下示例中,MyDate 以具有当前日期的 Date 对象开始,但日期输入不会获取该对象(它期望其格式为格式为 YYYY/MM/DD 的字符串)。

一旦您在选择器中选择了日期,MyDate 就会变成上述格式的字符串。

如何绑定它,以便 MyDate 保持 javascript Date 并由输入控件正确解释?

见见http://jsfiddle.net/LLkC4/3/:-

<input data-bind="value : MyDate" type="date">
<hr>   
<span data-bind="html: log" />

<script>
var viewModel = {    
    MyDate : ko.observable(new Date()),
    log : ko.observable(""),
    logDate : function () { 
            this.log(this.log() + this.MyDate() + " : " +
                     typeof(this.MyDate()) + "<br>");
                     }
};

viewModel.MyDate.subscribe(function (date) {    
    viewModel.logDate();    
});

ko.applyBindings(viewModel);

viewModel.logDate()
</script>

【问题讨论】:

    标签: javascript html knockout.js


    【解决方案1】:

    虽然@amakhrov 答案会起作用(如果使用像@Stijn sujested 这样的可写计算可观察对象会更好)我决定使用Custom Bindings 来做到这一点。

    这样做的主要优点是可重用性——我只需要在我想绑定它的任何地方使用data-bind="datePicker : MyDate"。我还可以修改输入元素的其他属性,因此如果绑定到复杂的 jQuery(和其他)控件。

    Read here for more pro/cons 关于做这种事情的 3 个选择)

    HTML

    <input data-bind="datePicker : MyDate" type="date">
    

    JS

    ko.bindingHandlers.datePicker = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {                    
            // Register change callbacks to update the model
            // if the control changes.       
            ko.utils.registerEventHandler(element, "change", function () {            
                var value = valueAccessor();
                value(new Date(element.value));            
            });
        },
        // Update the control whenever the view model changes
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            var value =  valueAccessor();        
            element.value = value().toISOString();
        }
    };
    
    var viewModel = {    
        MyDate : ko.observable(new Date())
    };     
    
    ko.applyBindings(viewModel);
    

    http://jsfiddle.net/LLkC4/5/

    【讨论】:

    • 这仍然对您有用吗?我的 chrome 日期选择器拒绝显示任何日期 - 即使我选择新日期并且即使它们出现在您的日志打印输出中,也只是在框中显示“mm/dd/yyyy”。你知道如何解决吗?我在挣扎。
    • @uosɐſ 这对我也不起作用,直到我开始在更新功能中调用 ko.unwrap(valueAccessor())
    • 不行,添加 viewModel.MyDate('2/2/2012');到小提琴的底部,看到选择器中的日期没有更新。
    • 以下新 ko 的更正答案
    【解决方案2】:

    您可以为模型中的日期对象使用计算变量:

    在html中:

    <input data-bind="value : rawDate" type="date">
    

    在代码中:

    var currentDate = (new Date()).toISOString().split('T')[0];
    
    // this is used instead of MyDate in the data binding
    rawDate : ko.observable(currentDate),
    
    ...
    // and then set up the dependent variable
    viewModel.MyDate = ko.computed(function () {
        var val = this.rawDate();
        if (typeof val === 'string') val = new Date(val);
    
        return val;
    }, viewModel)
    

    请看演示:http://jsfiddle.net/gcAXB/1/

    【讨论】:

    • 这不会在初始加载时设置选择器的值 - 怀疑必须将 rawDate 设置为 MyDate 的 ISO 格式字符串?
    • 你是对的。您的原始示例中没有显示日期,所以我也忘记添加了 :) 请查看更新的代码和上面的演示。
    • 感谢您的回答,您能否告诉我有关如何在不使用计算变量的情况下使用 knockout.js html5 datepicker(输入类型“日期”)的任何想法?就像在这里完成的那样:screencast.com/t/pMtuSV6mxUf
    【解决方案3】:

    这是一个适用于我的最新knockoutjs的解决方案,基于下面的链接并修改为具有自定义初始化函数来处理更新 ko.computed 属性作为您的日期值更改。

    请注意,utils.formatDate 只是一个实用函数,用于将日期格式化为您想要的任何字符串,所以只需将其替换为您自己的日期格式化代码,无论您使用的是 momentjs 还是其他东西。

    ko.bindingHandlers.date = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {    
            ko.utils.registerEventHandler(element, 'change', function () {
                var value = valueAccessor();
    
                if (element.value !== null && element.value !== undefined && element.value.length > 0) {
                    value(element.value);
                }
                else {
                    value('');
                }
            });
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            var value = valueAccessor();
            var valueUnwrapped = ko.utils.unwrapObservable(value);
    
            var output = '';
            if (valueUnwrapped !== null && valueUnwrapped !== undefined && valueUnwrapped.length > 0) {
                output = utils.formatDate(valueUnwrapped);
            }
    
            if ($(element).is('input') === true) {
                $(element).val(output);
            } else {
                $(element).text(output);
            }
        }
    };
    
        <div>
            <label>Date of Birth:</label>
            <input type="text" data-bind="date: dateOfBirth, format: 'DD MMM YYYY'" />
        </div>
    

    BINDING AND FORMATTING DATES USING KNOCKOUT AND MOMENT JS

    【讨论】:

    • 帖子中的链接无效,这是我的 MomentJS 代码:cederlof.net/…
    • 仅供参考,以供将来了解,Jason Mitchell 关于此的文章(上面的链接)现在位于 link(截至今天)
    【解决方案4】:

    现在使用 Moment.js 轻松多了

    this.sessionDate = ko.observable(moment().format('YYYY-MM-DD'));
    this.getFormattedDate = () => { return moment(this.sessionDate()'YYYY-MM-DD').format('MM/DD/YYYY') }; // Note this is ES2015 syntax
    

    在你的html中你可以绑定它

    <input class="form-control" name="date" type="date" id="date" data-bind="value: sessionDate">
    

    并将其显示为格式

    <p data-bind="text : getFormattedDate()">Loading Date</p>
    

    无需创建自定义绑定,您可以为旧版浏览器使用 shim。

    【讨论】:

    • 我认为这应该是新的答案,就像一个魅力。
    【解决方案5】:

    this custom binding,但使用momentJS:

    ko.bindingHandlers.datePicker = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            // Register change callbacks to update the model
            // if the control changes.
            ko.utils.registerEventHandler(element, "change", function () {
                var value = valueAccessor();
                value(moment(element.value).format());
            });
        },
        // Update the control whenever the view model changes
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            var value =  valueAccessor();
            element.value = moment(value()).format("YYYY-MM-DD");
        }
    };
    

    【讨论】:

    • 你能告诉我为什么当我在更新中更改格式时(比如: element.value = moment(value()).format("DD-MM-YYYY"); )它会更新我的数据采用 MM-DD-YYYY 格式?
    • 你能解释一下为什么如果我们有一个 'registerEventHandler' 'update' 仍然运行吗?
    • @petrosmm 当 viewmodel 值改变时触发更新代码,而当 DOM 值改变时触发事件处理程序,例如通过用户输入。
    【解决方案6】:

    根据上面 Ryan 的回答,这与较新的 ko/chrome 小部件一起使用会更好一些。它还会去除日期的时间部分。

    ko.bindingHandlers.datePicker = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            // Register change callbacks to update the model
            // if the control changes.
            ko.utils.registerEventHandler(element, "change", function () {
                var value = valueAccessor();
                var target_date = element.valueAsDate;
                var truncated = new Date(target_date.getFullYear(), target_date.getMonth(), target_date.getDate());
                value(truncated);
            });
        },
        // Update the control whenever the view model changes
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            var value =  valueAccessor();
            var unwrapped = ko.utils.unwrapObservable(value());
            if(unwrapped === undefined || unwrapped === null) {
                element.value = '';
            } else {
                element.valueAsDate = unwrapped;
            }
        }
    };
    

    【讨论】:

    • 我不得不使用 blur 事件而不是 change 事件来完成这项工作。 Chrome 44.0.2403.39 beta-m(64 位),knockout.js 3.10
    • 截至 2018 年 2 月 27 日,我无法得到这个答案。
    【解决方案7】:

    来自HTML 5 - Input type date formatting on iOS

    有两种格式在起作用:

    • 显示格式
    • 向 JavaScript 公开并发送到服务器的内部格式

    您无法更改显示格式。由浏览器决定 日期如何呈现给用户(实际上它由 系统的语言环境)。

    您也不能更改内部格式。它始终是 ISO8601, 无论浏览器/语言环境如何。

    您必须使用该特定格式预先填充它,并且您可以添加一个计算的 observable 以将其解析为 Date 对象,以便您可以在应用程序的其他位置读取它。

    如果您还想从 JS 中写入,您可以设置一个 writeable computed observable 并解析输入以查看它是来自输入字段的字符串,还是来自您的 JS 的 Date 对象。

    【讨论】:

      【解决方案8】:

      这对我有用

      ko.bindingHandlers.momentDate = {
      _parseDateTime: function (element, valueAccessor) {
          var value = valueAccessor();
          var valueUnwrapped = ko.utils.unwrapObservable(value);
          var datetime = moment(valueUnwrapped);
          var date = moment($(element).val(), 'YYYY-MM-DD');
          datetime = datetime.set({
              'year': date.get('year'),
              'month': date.get('month'),
              'date': date.get('date')
          });
          value(datetime.toDate());
      },
      init: function (element, valueAccessor) {
          function bind() {
              ko.bindingHandlers.momentDate._parseDateTime(element, valueAccessor);
          }
          $(element).change(bind).blur(bind);
      },
      update: function (element, valueAccessor) {
          var value = valueAccessor();
          var valueUnwrapped = ko.utils.unwrapObservable(value);
          var date = moment(valueUnwrapped);
          $(element).val(date.format('YYYY-MM-DD'));
      }
      

      };

      <input type="date" data-bind="momentDate: $data.Date" class="form-control"/>
      

      【讨论】:

        【解决方案9】:

        这可能已经晚了,但它可能会帮助那里的人。我正在使用LuxonDateTime 对象在我的应用程序中轻松管理日期和时区。这适用于 DateTime 对象:

        ko.bindingHandlers.dateTime = {
          // Register change callbacks to update the model if the control changes.
          init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            ko.utils.registerEventHandler(element, 'change', function () {
              const value = valueAccessor()
              const datetime = DateTime.fromFormat(element.value, 'yyyy-MM-dd')
              value(datetime)
            })
          },
          // Update the control whenever the view model changes
          update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            const value = valueAccessor()
            element.value = ko.unwrap(value).toFormat('yyyy-MM-dd')
          }
        }
        

        用法:

        // viewmodel
        const today = DateTime.fromFormat(DateTime.now().toFormat('yyyy-MM-dd'), 'yyyy-MM-dd')
        this.date = ko.observable(today)
        
        // template
        <input type="date" data-bind="dateTime: date">
        

        【讨论】:

          猜你喜欢
          • 2015-10-08
          • 1970-01-01
          • 1970-01-01
          • 2023-04-08
          • 1970-01-01
          • 2018-09-01
          • 1970-01-01
          • 1970-01-01
          • 2013-02-25
          相关资源
          最近更新 更多