Unchecking Radio using Knockout Binding

The behaviour of knockouts check binding for radio buttons is useful if you have want to assign the value of the input tag into a observable like:

<input type="radio" value="Invoice" data-bind="checked:paymentOption">

But sometimes it is handy to to let the observable represent a boolean that checks/unchecks the radio button depending on its value

<input type="radio" value="Invoice" data-bind="checked:isSelected">

First note that there is no event firing upon un-checking, the event that is involved with a radio button selection is in in fact a change event on the group. Soo… How can we get around this in our knockout application? One approach is by adding a click binding that points to the parent of the view models that the radio buttons represent, like so:

<input type="radio" value="Invoice" data-bind="click: $parent.selectChild">

The selectChild method could look something like this:

self.selectChild = function (childViewModel) {
    for (var index = 0; index < self.childViewModels.length; index++) {
        var child = self.childViewModels[index];
        if (child.isSelected()) {
            child.isSelected(false);
        }
    }

    childViewModel.isSelected(true);
    return true;
};

The “return true” statement is a really funky thing in knockout that signals that you want the default action to be performed as well.

This leaves you with the expected behavior, but there is one more thing to do to get the nice finishing. Even though you have a boolean that can add/remove the checked attribute on the radio button, the ui will not be updated. The cleanest way I have found to solve this is to use the jQuery’s attr and remove checked (I know, it is wired… but jQuery does more things than just removing the attribute).

So as an addition to the click-binding I wrote a custom binding for just this:

ko.bindingHandlers.radio = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        // if subscribe is not defined, it is likely that the valueAccessor is not an observable
        if (typeof valueAccessor().subscribe == "undefined")
            return;

        if ($(element).attr('type') != 'radio')
            return;

        // subscribe to the changes to the observable in the binding 
        valueAccessor().subscribe(function (newValue) {
            // if the new value is false, we interperate it as it should be unselected
            if (!newValue) {
                // ... so we update the ui with the jQuery hack
                $(element).attr("checked", false);
            } else {
                $(element).attr("checked", true);
            }
        });
    }
};

It subscribes to the observable in the binding-expression and when change, it adds/removes checked using jQuery. There is some safe-guarding there as well, so that it does nothing if the valueAccesor is not an observable and the DOM-element is not of type radio. This should prevent errors to occur.

With these pieces of the puzzle, the data-bind expression would be something like this:

<input type="radio" value="Invoice" data-bind="click: $parent.selectChild, radio: isSelected">

Happy coding!

Typeahead from Twitter Bootstrap data-bound in Knockout.js

After writing about Knockout.js and Twitter Bootstrap‘s Popover and Tooltip, it feels just about right that I continue to rant about how to get the two frameworks to get along. This time, it’s Typehead‘s time to get examined.

In general I like how Twitter Bootstrap works with the data-attributes when describing what I’m expecting from it, such as data-title for the title of a popover, or data-toggle for how the collapse function will disappear/reappear.

For Typeahead, I found it a bit frustrating that the data-source, that is used to populate the typeahead list with options, must be decleared as a list like so:

<input type="text" data-source="
[&quot;Alabama&quot;,&quot;Alaska&quot;,&quot;Arizona&quot;
,&quot;Arkansas&quot;,&quot;California&quot;,&quot;Colorado&quot;
,&quot;Connecticut&quot;,&quot;Delaware&quot;,&quot;Florida&quot;
,&quot;Georgia&quot;,&quot;Hawaii&quot;,&quot;Idaho&quot;,&quot;
Illinois&quot;,&quot;Indiana&quot;,&quot;Iowa&quot;,&quot;
Kansas&quot;,&quot;Kentucky&quot;,&quot;Louisiana&quot;,&quot;
Maine&quot;,&quot;Maryland&quot;,&quot;Massachusetts&quot;,&quot
;Michigan&quot;,&quot;Minnesota&quot;,&quot;Mississippi&quot;
,&quot;Missouri&quot;,&quot;Montana&quot;,&quot;Nebraska&quot;
,&quot;Nevada&quot;,&quot;New Hampshire&quot;,&quot;New Jersey&quot;
,&quot;New Mexico&quot;,&quot;New York&quot;,&quot;North Dakota&quot;
,&quot;North Carolina&quot;,&quot;Ohio&quot;,&quot;Oklahoma&quot;
,&quot;Oregon&quot;,&quot;Pennsylvania&quot;,&quot;Rhode Island&quot;
,&quot;South Carolina&quot;,&quot;South Dakota&quot;
,&quot;Tennessee&quot;,&quot;Texas&quot;,&quot;Utah&quot;
,&quot;Vermont&quot;,&quot;Virginia&quot;,&quot;Washington&quot;
,&quot;West Virginia&quot;,&quot;Wisconsin&quot;,&quot;Wyoming&quot;]"
 data-items="4" data-provide="typeahead" style="margin: 0 auto;">
(Example from Twitter Bootstraps).

That kind of code just makes the source very unreadable. Therefore,when I wrote the custom data-binding in Knockout, I added the possibility to describe the datasource as an ko.observableArray/javascript variable/objects property.Here’s what you would write if you had an observable array named colors that you wanted to use as data source for your typeahead:

<input type="text" data-bind="typeahead: colors"/>

Imagine that you had some list that is not part of your model (maybe it is not important to keep track on the list):

<script>
var allColors = ["Red", "Green", "Yellow", "Black"];
</script>

The databinding is described just the same, but with singel citation signs like this:

 <input type="text" data-bind="typeahead: 'allColors '"/>

Similarly, if you have an javascript object and you want one if its properties as datasource:

<script>
var myComplexObject = {
    animals: ["pig", "pony", "dog", "cat", "cow", "chicken", "fish", "fox"]
};
</script>

You just have to write:

<input type="text" data-bind="typeahead: 'myComplexObject.animals'"/>

Here’s the custom biding code, which can be tested in the fiddle.

ko.bindingHandlers.typeahead = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var typeaheadSource; // <-- this is where our typeahead options will be stored in
        //this is the parameter that you pass from you data-bind expression in the mark-up
        var passedValueFromMarkup = ko.utils.unwrapObservable(valueAccessor());
        if (passedValueFromMarkup instanceof Array) typeaheadSource = passedValueFromMarkup;
        else {
            // if the name contains '.', then we expect it to be a property in an object such as myLists.listOfCards
            var splitedName = passedValueFromMarkup.split('.');
            var result = window[splitedName[0]];
            $.each($(splitedName).slice(1, splitedName.length), function(iteration, name) {
                result = result[name];
            });

            // if we find any array in the JsVariable, then use that as source, otherwise init without any specific source and hope that it is defined from attributes
            if (result != null && result.length > 0) {
                typeaheadSource = result;
            }

        }
        if (typeaheadSource == null) $(element).typeahead();
        else {
            $(element).typeahead({
                source: typeaheadSource
            });
        }

    },
};

Popover from Twitter Bootstrap with Knockout.js observable

I explained earlier how to get Twitter Bootstrap‘s neat Tooltip feature to work with Knockout.js observables. For my current project I wanted to be able to edit the value of my observable property from within a Twitter Bootstrap Popover.

My goal was to be able to keep as much of the bootstrap functionality intact(e.g. the data-attributes should work) and also be able to combine tooltip databinding with other knockout bindings. In a nutshell, I wanted to be able to write something like

<span data-placement="bottom" data-title="write new text to update" data-bind="popover: '#editTemplate', text:exampleText"></span>

Where

  • #editTemplate is the Css Selector for a html-template that contains the desired popover content
  • exampleText is, just like when it comes to any other Knockout binding – just a property on the ViewModel.
  • data-placement and data-title are attributes that Twitter Bootstrap uses to render the Popover

I found this to be a bit tricky, since the popover content will typically be appended to the DOM after the ko.applyBindings(viewModel) is called. Therefore, the data-bindings will not be noticed by Knockout and the binding will not take place.

Luckely, it is quite a pleasure to extend Knockout with custom bindings. Here’s the binding that I wrote:

ko.bindingHandlers.popover = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var cssSelectorForPopoverTemplate = ko.utils.unwrapObservable(valueAccessor());
            var popOverTemplate = "<div id='my-knockout-popver'>" + $(cssSelectorForPopoverTemplate).html() + "</div>";
            $(element).popover({ content: popOverTemplate, html:true, trigger: 'manual' });

            $(element).click(function() {
                $(this).popover('toggle');
                var thePopover = document.getElementById("my-knockout-popver");
                ko.applyBindings(viewModel, thePopover);
            });  
        },
    };

There are a few things that can be made more nicely, like creating the surrounding <div> using jQuery’s wrap(). A limitation with this implementation is that the popover is toggled by click. This can easerly be solved by adding a data-trigger atttriute in the HTML and in the js method look at the element to see if it has that attribute and if so change the click-listener to a hover-listener etc.

 

The JsFiddel is found here.