1

(This is a follow up to this question)

In Knockout.js the textInput binding updates the model after every keystroke and allows me to implement a simple input mask. It works nicely by itself, but when the input field is inside a template, the field loses focus after every keystroke.

This fiddle demonstrates the problem. Notice that I needed some $parent trickery to make the input field work in both directions (read and write). If I use $data, it reads the new value when it's updated externally but doesn't write it to the model and doesn't apply the input mask.

https://jsfiddle.net/h0qh3ecc/4/

Simplified code follows:

HTML:

textInput (this one works):<br>
<input data-bind="textInput: formattedString" >
<div data-bind="text: formattedString"></div>
<hr>
template textInput ($parent loses focus / $data doesn't call write):<br>
<script type="text/html" id="input-standalone-template">
    <input data-bind="textInput: $parent.formattedString"> <input data-bind="textInput: $data">
</script>
<div data-bind="template: { name: 'input-standalone-template', data: formattedString }"></div>
<hr>

JavaScript:

var model = (function () {
    function format(str) {
        var undone = str.replace(/-/g, '');
        if (undone.length > 3) {
            undone = undone.substr(0,3) + '-' + undone.substr(3);
        }
        return undone;
    }

    var displayString = ko.observable('');
    var formattedString = ko.computed({
        read: function () {
            return displayString();
        },
        write: function (newValue) {
            var f = format(newValue);
            console.debug("Format", newValue, "=", f);
            displayString(f);
        }
    });

    return {
        formattedString: formattedString
    };
}());
ko.applyBindings(model)

Is this a bug? Is there a workaround?

Community
  • 1
  • 1
marcus
  • 5,041
  • 3
  • 30
  • 36

1 Answers1

0

I have just found out a workaround: I have to pass $data (or $parent, depending on page structure) instead of the field directly to the template data parameter. The template must be adjusted accordingly.

E.g, instead of

<!-- define template -->
<script type="text/html" id="my-input-template">
    <input data-bind="textInput: $data">
</script>

<!-- call template (passing field, causes focus issues) -->
<div data-bind="template: {
                    name: 'my-input-template',
                    data: formattedString
                }">
</div>

I should do

<!-- define template -->
<script type="text/html" id="my-input-template">
    <input data-bind="textInput: formattedString">
</script>

<!-- call template (passing $data, works fine) -->
<div data-bind="template: {
                    name: 'my-input-template',
                    data: $data
                }">
</div>

For some obscure reason this change fixes the problem.

marcus
  • 5,041
  • 3
  • 30
  • 36