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!

Best practice: Knockout observableArray inplace Add/Edit

The scenario is familiar: you have an observableArray in the viewModel that you want to be able to push new items to, or perhaps modify existing elements. There are just tons of code out there how to do so, for example knockout official example pages.

However, by mimicking that pattern, the viewModel quickly turns into a huge chunk of pointer to “active” elements, methods for “start edit”, “stop edit”. In the project I’m working, we’re going to have three different list with this functionality and it would just be such an repetitive work and overly complex code.

Just to make things worse, let’s imagine you want an in place editable table, like this guy wants to. If you look at his markup, there is just too much happening there with brackets, dubble equal signs,… Ugh, from my point of view the markup is the most important thing to get understandable and that’s not it.

Here’s the problem

Meet the Offers, an array with stuff that I’m offering. Yep, you guessed right it is an observableArray. As an admin, I want to be able to add/remove/edit Offers in place.

What you might have seen around is that an offer is created from a function call like so:

function Offer(id,name, shortName){
     this.id = ko.observable(id);
     this.name = ko.observable(name);
 }

Somewhere in your markup you end up with a

<input type="button" data-bind="click:$edit">

That must correlate to method on your viewModel

var viewModel = new function () {
    var self = this;
    self.Offers = ko.observableArray([]);
    self.selectedOffer = ko.observable();

    self.edit = function (item) {
        self.selectedOffer(item);
    };
}

But, hang on you also want to show an input text field if you edit the current item, as opposed to a div/span if you don’t edit. So you need some more methods that I am only going to name:

  • isOfferActive (used in the foreach in markup to evaluate what to show)
  • saveOffer (used to set selectedOffer to null so that none is edited)

Here’s the solution

So what we want to do is putting some of the “is active” “is inactive” logic on the element itself (rather than a comparing method on the viewModel $root). In the light of what I wrote earlier about the module pattern and jasmine testing, I ended up with this class:

var Offer = (function () {
    var activeOffer = function() {};

    //CONSTRUCTOR
    var offer = function(name, productMasterId, active) {

        var self = this;
        activeOffer = active;
        self.Name = ko.observable(name);
        self.Id = ko.observable(productMasterId);

        active(this);
        self.IsActive = ko.computed(function () {
            return self == active();
        });

        self.IsNotActive = ko.computed(function () {
            return self != active();
        });
    };

    var activateOffer = function () {
        activeOffer(this);
    };

    var deactivateOffer = function () {
        activeOffer(null);
    };

    offer.prototype = {
        constructor: offer,
        activateOffer: activateOffer,
        deactivateOffer: deactivateOffer
    };

    return offer;
}())

What’s so great with this? Well, it it takes a pointer to an observable that is considered to be the placeholder for the “current” selected Offer. The offer is then responsible to either set itself as active (offer.activateOffer()) or deactivate (offer.deactiveOffer()). Note that this observable does not need to be attached to the viewModel, actually this is how i do the add:

var viewModel = new function () {

    var activeUnlockedOffer = ko.observable(""); //we do not even need to add this to the viewModel

    this.Offers = ko.observableArray();

    this.addOffer = function () {
        var offer = new Offer("", "", activeUnlockedOffer);
        viewModel.Offers.push(offer);
    };

    this.removeOffer = function (data) {
        viewModel.Offers.remove(data);
    };
}

As you can see the activeUnlockedOffer is just a local variable. No need to pollute the viewModel with that. The only additional methods in my viewModel is the add/remove – which I think suits there (it should not be at the Offer anyways).

Perk number two, the markup will be easier to read, look at this:

<button title="Edit" data-bind="click: $data.activateOffer, visible: $data.IsNotActive" />
<button title="Save" data-bind="click: $data.deactivateOffer, visible: $data.IsActive" />

Another sweet thing with this solution is that when I add another list off offers, let’s call it OffersOnSales I can decide if I should allow one editable row in the two tables (in that case I would initialize all offers with the same observable), or if I want to allow the editing of one OfferOnSale and one normal Offer (in that case I would initialize the different groups with different observables).

And… yes, there’s a jsFiddle to it. Happy coding!

Working with jQuery and Html Templates

When working with javascript frameworks such as Mustasch.js you usually want to have you html templates in your html-file or in a html template file (you definitly don’t want it as a string somewhere in your javascripts). I had an validation error message template that I wanted to populate with validation messages:

<script type="text/template" id="validation-alert-content">
    <div>
        <button type="button" data-dismiss="alert">×</button>
        <p>Default Error</p>
    </div>
</script>

The server side validation was called with ajax. Depending on the response, I wanted to customize the content of my alert, e.g. change the Default Error text.

Strangely enough, when I did this:

var createAlertWithContent = function(content) {
    var alertContainer = $('#validation-alert-content');
    var myParagraph = alertContainer.find('p');
    $(myParagraph).text(content);
    return alertContainer.html();
};

The default error text was displayed. It became obviose that the content alertContent was not an html element, but a string. Therefore

alertContainer.find('p')

Gave me just an empty array. One solution to the problem was to change type of my template from <script> to <div>. However, I didn’t like the idea of a div at my page that held error message content without nothing being wrong. What is the semantic in that?

However

$(alertContainer.html()).find('p')

Gave me the paragraph that I was looking for. Another problem arouse, that it looks like a few other people have tried to solve. I must say that I do not like the proposed solution, but I figured I rather hide that ugly hack somewhere deep in my javascript, and that way keep the html template beautiful.

Here’s the result:

var createAlertWithContent = function(content) {
    var scriptTemplate = $('#validation-alert-content');
    var alertContainer = $('<div>' + scriptTemplate.html() +'</div>');
    var myParagraph = alertContainer.find('p');
    $(myParagraph).text(content);
    return alertContainer.html();
};