Search code examples
javascriptknockout.jsdom-eventsknockout-validation

Writable computed variables does not read value when copying same value twice


I am formatting a input into currency and using writable computed variables to update the value back to textbox.

e.g. I have a value 1234.12

I am copying the value from notepad and pasting it into the textbox, on tab out it is hitting the read function and getting formatted into currency and getting written back to textbox as $1,234. When I am pasting same value 1234, its is not hitting the read, directly getting written as it is, in to the textbox as 1234 on tab out.

I have seen this problem in js also.

Do you have any idea how to format the value if I paste the same value multiple times.


Solution

  • You can use the { notify: "always" } extender to ensure your data always gets formatted.

    In the example below:

    • The _backingValue contains "1234"
    • When inputing "1234" in the <input>, the computed writes "1234" to the _backingValue
    • Under normal conditions, the _backingValue would not notify any subscribers of a value change, since "1234" === "1234. However, because we explicitly told it to always trigger a valueHasMutated "event", it does notify its subscribers.
    • formattedValue's read property has a dependency on _backingValue.
    • Because _backingValue notifies us it has changed, the formatting function will run again, outputting "1234$".
    • Under normal conditions, formattedValue would not notify any subscribers of a value change, since "1234$" === "1234$". Again however, because of the extension, a valueHasMutated is triggered.
    • The <input>'s value binding receives an update and renders "1234$" to the screen.

    const _backingValue = ko.observable("1234")
      .extend({ notify: "always" });
    
    const formattedValue = ko.computed({
      read: () => _backingValue().replace(/\$/g, "") + "$",
      write: _backingValue
    }).extend({ notify: "always" });
    
    
    ko.applyBindings({ formattedValue });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <input data-bind="value: formattedValue">