用于复合控制的自定义Knockout绑定

时间:2015-02-08 13:52:06

标签: knockout.js

我正在寻找一个自定义绑定的例子(如果可能的话,jsfiddle)多年前​​我们曾经称之为“复合组件”。我真的不希望有人为我编写代码。在我投入大量时间之前,我只想查看几个样本,但我找不到任何样本。大多数自定义绑定示例仅限于一种方式。只是询问你是否知道类似的样本......

大多数HTML / CSS都是由设计团队提供给我的,所以布局并不总是我的选择。在这种情况下,日期,电话号码和社会保障输入都是使用共同的“主题”创建的。那个主题是在div中有三个独立的元素。例如,日期有一个月,一个是一天,一个是一年。 (我不认为我们不需要将它们与旋转器一起复杂化)。预计会有验证和最大/最小输入限制。

我按如下方式创建了一个对象

var SplitDate = function (date) {
    var self = this;
    self.month = ko.observable();
    self.day = ko.observable();
    self.year = ko.observable();

    var momentDate = moment(date);

    if (momentDate.isValid()) {
        self.month(momentDate.month() + 1);
        self.day(momentDate.day());
        self.year(momentDate.year());
    }
}

在我的viewmodel中,我尝试了两种

self.dateOfBirth = new SplitDate(myDate);

self.dateOfBirth = ko.observable(new SplitDate(myDate));

但这些都没有使用标准值绑定(例如

)正确绑定
data-binding = "value: dateOfBirth.day" or data-binding = "value: dateOfBirth().day"

所以我假设我需要一个自定义绑定。我不确定什么是最好的方法,如果所有这些都需要是可观察的。我们正在使用Knockout验证,所以我也希望我能为isDalid()添加一个函数给SplitDate。

所以我的问题是,在我花了几个小时摸索这个之前,有没有人有一个好榜样?

1 个答案:

答案 0 :(得分:1)

您的SplitDate对象将月,日,年作为单独的组件进行跟踪。听起来像你在尝试使用完整日期设置它们并且遇到麻烦。我怀疑你做的是这样的事情:

self.dateOfBirth = new SplitDate(newDate);

这不起作用,您的视图绑定到之前SplitDate中的可观察对象。您需要做的是更新日期组件。


然而,我认为你正在犯这个错误。拆分日期应该是完整日期的包装,而不仅仅是某些日期组件。您只需要为要支持的不同字段提供访问者。在淘汰赛中,您需要一个可观察的完整日期,然后计算不同领域的观察值。

function SplitDate(date) {
    var _date = ko.observable(); // the backing field
    setDate(date);

    // we can intercept set attempts to validate
    this.date = ko.computed({ read: _date, write: setDate });
    this.year = ko.computed({ read: makeGetter('year'), write: makeSetter('year') });
    // we need to special case months to offset to be 1-based
    this.month = ko.computed({ read: getMonth, write: setMonth });
    // the date is the day of the month, day is day of the week
    this.day = ko.computed({ read: makeGetter('date'), write: makeSetter('date') });

    function setDate(date) {
        var momentDate = moment(date);
        if (momentDate.isValid()) _date(momentDate);
    }
    function makeGetter(field) {
        var getter = moment.fn[field];
        return function () {
            var date = _date();
            if (date) return getter.call(date);
        };
    }
    function makeSetter(field) {
        var setter = moment.fn[field];
        return function (value) {
            var date = _date();
            if (date) {
                setter.call(date, value);
                // we modified the underlying value, notify
                _date.valueHasMutated();
            }
        };
    }
    // we need to offset months
    function getMonth() { // 1-12
        var date = _date();
        if (date) return date.month() + 1;
    }
    function setMonth(month) { // 1-12
        var date = _date();
        if (date) {
            date.month(month - 1);
            _date.valueHasMutated();
        }
    }
}

使用此方法,可以将日期的任何组成部分的值更改为常规可观察值。

self.dateOfBirth = new SplitDate(date);
self.dateOfBirth.month(8); // August
self.dateOfBirth.date(newDate);

您应该能够单独绑定到这些字段,并且字段上的任何更新都会像您期望的那样更新基础日期。

<input type="text" data-bind="value: dateOfBirth.date"/>
<input type="text" data-bind="value: dateOfBirth.year"/>
<input type="text" data-bind="value: dateOfBirth.month"/>
<input type="text" data-bind="value: dateOfBirth.day"/>

fiddle