Building a Knockout.js Extender for Boolean Values

Categories: JavaScript

Tags: Knockoutjs

Many times in my UI, I have a pair of radio buttons that represent two Boolean values such as a question that has a Yes/No answer that the user must make.  By default, a KO observable bound to these radio buttons will be set to the value of the input element that represents the radio button.  For these types of situations, this is not what I want.  What I really want to do is have the underlying value of the observable stay as a Boolean value (true/false) rather than as a string.   I want this because this underlying value is what I want to POST to the server.  Even though radio buttons deal in string values, they are actually modifying a Boolean value in this case.

What we need is a Knockout.js extender that adds new functionality to these specific observables so that the underlying Boolean value can be preserved.  Here’s the code:

ko.extenders["booleanValue"] = function (target) {
    target.formattedValue = ko.computed({
        read: function () {
            if (target() === true) return "True";
            else if (target() === false) return "False";
        },
        write: function (newValue) {
            if (newValue) {
                if (newValue === "False") target(false);
                else if (newValue === "True") target(true);
            }
        }
    });

    target.formattedValue(target());
    return target;
};

This code uses a technique that I really like.  It attaches a KO computed observable as a property of the actual observable (in this case, target).  This sub-observable is called formattedValue and this is what you actually bind to in your HTML data-bind attribute.  This technique is cool because this observable is hidden away from ko.toJS() and other functions that don’t see it.  When you serialize your observable back to the raw data values you want to POST, it melts away. 

Since the formattedValue computed is a writeable computed, you can read and write to it.  The read function will test the underlying observable (remember this is a true JS Boolean value).  It will then convert this to a string value (by convention, “True” or “False”).  The write function will do the opposite: it will test the incoming string value (this is from the HTML control), and will convert to the proper Boolean value on the actual observable.

To use the extender on your model, you’d do something like the following:

function Order() {
  this.wantsFries= ko.observable(false).extend({ booleanValue: null });
}

The answer property now has a sub-observable (formattedValue) that can be bound to a series of radio buttons to get the correct behavior:

<span>Do you want fries with that?</span>
<label>
  <input type="radio" name="question" value="True" 
             data-bind="value: wantsFries.formattedValue" /> Yes
</label>
<label>
  <input type="radio" name="question" value="False" 
             data-bind="value: wantsFries.formattedValue" /> No
</label>

When the user clicks one of the radio buttons, the correct true/false value will be sent into the underlying observable and is ready to be unwrapped and sent to your server.  The main point of this example, I believe, is the power in these sub-observables.  I’ve found this technique to be very clean and useful.

No Comments