javascript - formatter.js 覆盖 knockout 值

标签 javascript knockout.js

我使用 knockout.js 来绑定(bind)要查看的值。 当显示模式时,我初始化格式化程序。这是示例:

<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中有空值

最佳答案

将 knockout 与执行任何类型 DOM 操作的库一起使用 - 包括元素上的值更新 - 需要 custom binding handler ,这样 knockout 就可以 a) 正确初始化该库,b) 在 View 模型和 View 之间传递任何更新。

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

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

为了能够做到这一点,我们必须连接格式化程序的内部函数之一 - _processKey method 。只要输入的值发生变化,就会调用此方法,因此它是设置一个小“告密者”的最佳位置,可以在值发生变化时通知 knockout 。

免责声明这是一个黑客行为。只要 formatter.js 内部发生变化,它就会中断。然而,在当前版本 0.1.5 中,它似乎运行得相当好。

这样我们就可以像这样绑定(bind)我们的 View :

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

someObservable 发生变化时,knockout 可以填充输入值,并且由于 Hook 到 _processKey,它还可以在每当 someObservable 发生变化时更新 someObservable输入值发生变化。

绑定(bind)处理程序的完整实现如下(它没有 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
};

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

现场演示(展开以运行):

// 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');
    }
});
input {
    width: 20em;
}
<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>

关于javascript - formatter.js 覆盖 knockout 值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39139555/

相关文章:

javascript - AngularJS 在文本区域中渲染 HTML

javascript - $.post(...) 无法识别 JSON 主体

javascript - knockout validation 和 Qtip

knockout.js - 两个带 knockout 的日期选择器,一个更新另一个

javascript - 当 Javascript 更改输入值时更新可观察对象

javascript - JavaScript调用/执行后是否需要删除回调函数?

javascript - 具有自己查询的 Gatsby 可重用组件

javascript - 如何取消对话框中的自定义 window.onscroll 函数,恢复正常行为?

javascript - 从 iframe 中的图像在主窗口中弹出灯箱

javascript - 使用 knockout.js 构建 Accordion