Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

knockout.js and disabling anchor tag

How can I disable and enable an anchor tag with this custom binding. It works great with input elements but the anchor tag just changes the CSS, not the disabling.

<a href="link" data-bind="myDisabled: !enabled()"/>

ko.bindingHandlers.myDisabled = {
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        ko.bindingHandlers.css.update(element, function() {return { disabled: value }; });
        ko.bindingHandlers.disable.update(element, valueAccessor);
    }
};
like image 392
Mike Flynn Avatar asked May 30 '12 16:05

Mike Flynn


2 Answers

You need to capture click event in your binding handler.

HTML:

<a href="link" data-bind="disableClick: !enabled()">test</a>
<br/><br/><br/>
<input type="checkbox" data-bind="checked: enabled"> enabled ​

JavaScript:

ko.bindingHandlers.disableClick = {
    init: function (element, valueAccessor) {

        $(element).click(function(evt) {
            if(valueAccessor())
                evt.preventDefault();
        });

    },

    update: function(element, valueAccessor) {        
        var value = ko.utils.unwrapObservable(valueAccessor());
        ko.bindingHandlers.css.update(element, function() {return { disabled_anchor: value }; });
    }
};

ko.applyBindings({ enabled: ko.observable(false)});

Here is a working example:

http://jsfiddle.net/kp74u/54/

UPDATE 1: If you need to prevent other event handlers bound after knockout binding handler was attached, you need to add stopImmediatePropagation to the event handler along with preventDefault.

example: http://jsfiddle.net/kp74u/55/

UPDATE 2: If you want to disable all event handlers (along with click event handlers attached before your binding handler, you need to 'hack' the jquery events array). Please note that this may not work other versions of jquery (example uses 1.7):

ko.bindingHandlers.disableClick = {
    init: function(element, valueAccessor) {

        $(element).click(function(evt) {
            alert('test before');
        });

        $(element).click(function(evt) {
            if (valueAccessor()) {
                evt.preventDefault();
                evt.stopImmediatePropagation();
            }
        });

        //begin of 'hack' to move our 'disable' event handler to top of the stack
        var events = $.data(element, "events");
        console.log(events);
        var handlers = events['click'];

        if (handlers.length == 1) {
            return;
        }

        handlers.splice(0, 0, handlers.pop());
        //end of 'hack' to move our 'disable' event handler to top of the stack


        $(element).click(function(evt) {
            alert('test after');
        });
    },

    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        ko.bindingHandlers.css.update(element, function() {
            return {
                disabled_anchor: value
            };
        });
    }
};

example: http://jsfiddle.net/nickolsky/kp74u/40/

UPDATE 3: As was mentioned there (suggested edit by FIR55TORM, sorry can't approve this completely correct edit because I am too late to review): if you're using jQuery 1.10.x, you will need to add an underscore to access the 'data' object like so:

var events = $._data(element, "events"); 

Revised fiddle for jQuery 1.10.x: http://jsfiddle.net/nickolsky/kp74u/41/

like image 93
Artem Avatar answered Sep 19 '22 17:09

Artem


I found this answer when goggling for a way of doing this, but I didn't like the approach so did my own

var orgClickInit = ko.bindingHandlers.click.init;
ko.bindingHandlers.click.init = function (element, valueAccessor, allBindingsAccessor, viewModel) {
    if (element.tagName === "A" && allBindingsAccessor().enable != null) {
        var disabled = ko.computed(function () {
            return ko.utils.unwrapObservable(allBindingsAccessor().enable) === false;
        });
        ko.applyBindingsToNode(element, { css: { disabled: disabled} });
        var handler = valueAccessor();
        valueAccessor = function () {
            return function () {
                if (ko.utils.unwrapObservable(allBindingsAccessor().enable)) {
                    handler.apply(this, arguments);
                }
            }
        };

    }
    orgClickInit(element, valueAccessor, allBindingsAccessor, viewModel);
};

Its seamless with the native click and enable binding (disable binding not implemented) Fiddle (Fiddle also uses my Convention over configuration library) http://jsfiddle.net/xCfQC/30/

like image 43
Anders Avatar answered Sep 21 '22 17:09

Anders