Enable/Disable jQuery buttons in Knockout with a Custom Binding Handler

Still working on those jQuery buttons, trying to update old ASP.Net Webforms using jQuery, Knockout, and Amplify. New problem today.

I was having problems getting Knockout to enable/disable my jQuery buttons using the Knockout ‘enable’ bindingHandler. It would enable/disable the underlying element that I had run the .button() method on, but it had no idea about the div that jQuery had wrapped my element in, or how to handle it.

So I wrote a custom bindingHandler for Knockout to handle this case. It also can handle non-jQuery elements as well, so you could change the declaration from ‘jEnable’ to ‘enable’, and this would work as a all-comers enable function. However, since this method uses jQuery (and therefore is much expensive than the plain old ‘enable’), I figured the extra binding was the best approach.

if (ko && ko.bindingHandlers) {
    ko.bindingHandlers['jEnable'] = {
        'update': function(element, valueAccessor) {
            var value = ko.utils.unwrapObservable(valueAccessor());
            var $element = $(element);
            $element.prop("disabled", !value);

            if ($element.hasClass("ui-button")) {
                $element.button("option", "disabled", !value);
            }
        }
    };
}

An example on how to use this:


<input id="btnToEnable" type="button" data-bind="jEnable: isEnabled" />

<script>
   $("#btnToEnable").button();
   var viewModel = { isEnabled: ko.observable(true) };
   ko.applyBindings(viewModel);
</script>

A gist of this code is located here.

Advertisements

Auto Creation of jQuery Buttons using Knockout Templates

While converting ASP.NET Webforms to be more clienty using HTML 5, Knockout, and jQuery, I came across a problem.

I want to use jQuery buttons on my Knockout-rendered rows, but whenever a new row gets added via a template, the buttons were not being created as jQuery buttons.

The issue was, I was calling a method to create the buttons after the page was fully rendered, but never again. So all the new rows wouldn’t have the .button method run on them, and thus no sparkly jQuery buttons.

What to do? I’ve got button markup that looks like this:

<div class="jButton" data-icon-name="refresh" data-bind="click: refresh">
    Refresh
</div>

And some existing code, that given an element with class “jButton” and optionally the data-icon-name set the icon you want, creates buttons out of divs.

I saw one person handle this via a new binding on the button, but I didn’t want to have to change to my existing template to get the behavior I was looking at.

I tried a couple of different options, and while looking through the Knockout examples for other options came across the afterAdd option in the template binding.

So a quick change to my template binding:

<tbody data-bind="template: {name:'rowCostItem', 
                             foreach: CostItems, 
                             afterAdd: function(elem) { var row = $(elem);
                                                        Buttons.createFrom(row); }}">

And now I get nice jQuery buttons for all my new rows.

P.S. The heart of the Buttons.createFrom() method:

    var btn = $(this);
    var iconName = btn.data("iconName");
    if (iconName) {
        btn.button({ icons: { primary: 'ui-icon-' + iconName} })
    } else {
        btn.button();
    }