formatter.js覆盖了淘汰值

时间:2016-08-25 07:50:19

标签: javascript knockout.js

我使用knockout.js来查看绑定值。 当显示模态时,我初始化格式化程序。这是样本:

<input type="text" id="propertyName" class="form-control" name="name" required="" data-bind="value: Name">

$("#exampleFormModal").on("shown.bs.modal", function () {
        self.InitFormatter();
    });

    self.InitFormatter = function () {
        $('#propertyName').formatter({
           'pattern': '{{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}}',
           'persistent': true
        });
    }

问题是value: Name

中有空值

1 个答案:

答案 0 :(得分:2)

将knockout与执行任何类型DOM操作的库(包括元素值更新)一起使用需要custom binding handler,以便敲除可以a)正确初始化该库并且b)在viewmodel和view之间传递任何更新

为formatter.js编写自定义绑定处理程序很棘手,因为formatter.js非常严格地控制输入元素上发生的所有与值相关的事件(键盘,粘贴) - 而不会暴露任何自己的事件。 / p>

换句话说,它很容易设置,但是当值发生变化时很难收到通知。但这正是使视图模型保持最新所必需的。

为了能够做到这一点,我们必须挂钩格式化程序的一个内部函数 - _processKey method。只要输入的值发生变化,就会调用此方法,因此它是设置一个小小的错误的最佳位置。当价值发生变化时告诉淘汰赛。

免责声明这是一个黑客攻击。每当formatter.js内部更改时它都会中断。然而,使用当前版本0.1.5,它似乎工作得相当好。

这样我们可以像这样绑定我们的视图:

<input data-bind="formatter: {
    value: someObservable,
    pattern: '{{9999}}-{{9999}},
    persistent: true
}">

和knockout可以在someObservable更改时填写输入值,并且由于挂钩_processKey,只要输入值发生变化,它也可以更新someObservable

绑定处理程序的完整实现如下(它没有jQuery依赖):

// ko-formatter.js
/* global ko, Formatter */

ko.bindingHandlers.formatter = {
    init: function (element, valueAccessor) {
        var options = ko.unwrap(valueAccessor()) || {},
            instance = new Formatter(element, ko.toJS(options)),
            _processKey = Formatter.prototype._processKey,
            valueSubs, patternSubs, patternsSubs;

        if (ko.isWritableObservable(options.value)) {
            // capture initial element value
            options.value(element.value);
            // shadow the internal _processKey method so we see value changes
            instance._processKey = function () {
                _processKey.apply(this, arguments);
                options.value(element.value);
            };
            // catch the 'cut' event that formatter.js originally ignores
            ko.utils.registerEventHandler(element, 'input', function () {
                options.value(element.value);
            });
            // subscribe to options.value to achieve two-way binding
            valueSubs = options.value.subscribe(function (newValue) {
                // back out if observable and element values are equal
                if (newValue === element.value) return;
                // otherwise reset element and "type in" new observable value
                element.value = '';
                _processKey.call(instance, newValue, false, true);
                // write formatted value back into observable
                if (element.value !== newValue) options.value(element.value);
            });
        }
        // support updating "pattern" option through knockout
        if (ko.isObservable(options.pattern)) {
            patternSubs = options.pattern.subscribe(function (newPattern) {
                instance.resetPattern(newPattern);
            });
        }
        // support updating "patterns" option through knockout
        if (ko.isObservable(options.patterns)) {
            patternsSubs = options.patterns.subscribe(function (newPatterns) {
                instance.opts.patterns = newPatterns;
                instance.resetPattern();
            });
        }
        // clean up after ourselves
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            if (valueSubs) valueSubs.dispose();
            if (patternSubs) patternSubs.dispose();
            if (patternsSubs) patternsSubs.dispose();
        });
    }
    // this binding has no "update" part, it's not necessary
};

这也支持使pattern可观察,因此您可以动态更改输入字段的模式。

现场演示(展开运行):

&#13;
&#13;
// ko-formatter.js
/* global ko, Formatter */

ko.bindingHandlers.formatter = {
    init: function (element, valueAccessor) {
        var options = ko.unwrap(valueAccessor()) || {},
            instance = new Formatter(element, ko.toJS(options)),
            _processKey = Formatter.prototype._processKey,
            valueSubs, patternSubs, patternsSubs;

        if (ko.isWritableObservable(options.value)) {
            // capture initial element value
            options.value(element.value);
            // shadow the internal _processKey method so we see value changes
            instance._processKey = function () {
                _processKey.apply(this, arguments);
                options.value(element.value);
            };
            // catch the 'cut' event that formatter.js originally ignores
            ko.utils.registerEventHandler(element, 'input', function () {
                options.value(element.value);
            });
            // subscribe to options.value to achieve two-way binding
            valueSubs = options.value.subscribe(function (newValue) {
                // back out if observable and element values are equal
                if (newValue === element.value) return;
                // otherwise reset element and "type" new observable value
                element.value = '';
                _processKey.call(instance, newValue, false, true);
                // write formatted value back into observable
                if (element.value !== newValue) options.value(element.value);
            });
        }
        // support updating "pattern" option through knockout
        if (ko.isObservable(options.pattern)) {
            patternSubs = options.pattern.subscribe(function (newPattern) {
                instance.resetPattern(newPattern);
            });
        }
        // support updating "patterns" option through knockout
        if (ko.isObservable(options.patterns)) {
            patternsSubs = options.patterns.subscribe(function (newPatterns) {
                instance.opts.patterns = newPatterns;
                instance.resetPattern();
            });
        }
        // clean up after ourselves
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            if (valueSubs) valueSubs.dispose();
            if (patternSubs) patternSubs.dispose();
            if (patternsSubs) patternsSubs.dispose();
        });
    }
    // this binding has no "update" part, it's not necessary
};

// viewmodel implementation

ko.applyBindings({
    inputPattern: ko.observable('{{9999}}-{{9999}}-{{9999}}-{{9999}}'),
    inputValue: ko.observable(),
    setValidValue: function () {
        var dummy = this.inputPattern().replace(/\{\{([a9*]+)\}\}/g, function ($0, $1) {
            return $1.replace(/\*/g, "x");
        });
        this.inputValue(dummy);
    },
    setInvalidValue: function () {
        this.inputValue('invalid value');
    }
});
&#13;
input {
    width: 20em;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/formatter.js/0.1.5/formatter.min.js"></script>

View:<br>
<input data-bind="formatter: {
    value: inputValue,
    pattern: inputPattern,
    persistent: true
}">
<input data-bind="value: inputPattern"><br>
<button data-bind="click: setValidValue">Set valid value</button>
<button data-bind="click: setInvalidValue">Set invalid value</button>

<hr>
Viewmodel:<br>
<pre data-bind="text: ko.toJSON($root, null ,2)"></pre>
&#13;
&#13;
&#13;