复合控件的自定义敲除绑定

Custom Knockout binding for Compound Control

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

大部分 HTML/CSS 都是设计团队给我的,所以布局并不总是我的选择。在这种情况下,日期、phone 数字和社会保障输入都是使用共同的“主题”创建的。该主题是在 div 中包含三个独立的元素。例如,日期有一个代表月,一个代表日,一个代表年。 (我认为我们不需要因为他们是纺纱厂而使这件事复杂化)。需要验证和 max/min 输入限制。

我创建了一个对象如下

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());
    }
}

在我的视图模型中,我都试过了

self.dateOfBirth = new SplitDate(myDate);

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

但是这些都没有使用标准值绑定正确绑定,例如

data-binding = "value: dateOfBirth.day" or data-binding = "value: dateOfBirth().day"

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

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

您的 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