knockout.js和禁用锚点标签

12

如何使用此自定义绑定禁用和启用锚点标签。它对输入元素非常好用,但是对于锚点标签只会改变CSS样式而不会禁用。

<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);
    }
};

我只看过通过将锚点的onclick设置为false来“禁用”锚点标签。您所说的“禁用”锚点的意思是什么? - Kyeotic
只是为了确保它不触发任何点击事件,并且在启用时,点击事件会重新启用。 - Mike Flynn
1
作为替代方案,您可以使用KOs注释逻辑来生成不同的标记。请参见:https://dev59.com/t2Qo5IYBdhLWcg3wiv0i - Spencer Ruport
4个回答

11
您需要在绑定处理程序中捕获点击事件。 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)});

这里是一个工作示例:

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

更新1: 如果您需要防止在绑定knockout处理程序之后绑定的其他事件处理程序,则需要将stopImmediatePropagation添加到事件处理程序中,以及preventDefault

示例:http://jsfiddle.net/kp74u/55/

更新2: 如果您想禁用所有事件处理程序(包括在您的绑定处理程序之前附加的单击事件处理程序),则需要“黑客”jquery事件数组。 请注意,这可能无法在其他版本的jquery中运行(示例使用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
            };
        });
    }
};

例子: http://jsfiddle.net/nickolsky/kp74u/40/

更新3: 如FIR55TORM所建议的编辑所提到的,如果你正在使用jQuery 1.10.x版本,你需要在访问"data"对象时添加下划线,像这样:

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

为jQuery 1.10.x修订的fiddle: http://jsfiddle.net/nickolsky/kp74u/41/


从jQuery 1.8开始,您不能再通过该API访问数据。请改为使用jQuery._data(key, value) - zombiehugs

1

我在谷歌搜索这个问题的解决方法时找到了这个答案,但我不喜欢这种方法,所以我自己想了一个。

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);
};

它与本地点击和启用绑定无缝结合(禁用绑定未实现) Fiddle(Fiddle还使用了我的约定优于配置库) http://jsfiddle.net/xCfQC/30/


非常喜欢这个想法,主要缺点是必须定义一个“click”方法才能使其工作。请查看我的答案,其中提供了一种即使未定义click也能正常工作的解决方案。 - Edyn

0

在@Anders的答案的启发下,我想出了自己的版本。允许使用带或不带“click”的“enable”、“disable”。还允许自定义禁用类,否则默认为“disabled”。

var koEnableUpdateOrig = ko.bindingHandlers.enable.update;
ko.bindingHandlers.enable.update = function (element, valueAccessor, allBindings) {
    // call original enable update
    var result = koEnableUpdateOrig.apply(this, arguments);
    var enabled = ko.unwrap(valueAccessor());

    // get and apply disabled class
    var disabledClass = "disabled";
    if (allBindings)
        disabledClass = allBindings().disabledClass || "disabled";
    if (enabled) {
        $(element).removeClass(disabledClass);
        if (element.tagName === "A")
            $(element).off("click.koEnableUpdate");
    }
    else {
        $(element).addClass(disabledClass);
        if (element.tagName === "A")
            $(element).on("click.koEnableUpdate", function (e) { e.preventDefault(); });
    }

    return result;
};
ko.bindingHandlers.disable.update = function (element, valueAccessor, allBindings) {
    // call enable with the reverse value
    // the original knockout disable does this, but does not pass the allBindings
    ko.bindingHandlers.enable.update(element, function () {
        return !ko.unwrap(valueAccessor())
    }, allBindings);
};

var koClickInitOrig = ko.bindingHandlers.click.init;
ko.bindingHandlers.click.init = function (element, valueAccessor, allBindings) {
    // wrap click function with enable/disable check
    var valueAccessorOrig = valueAccessor();
    valueAccessor = function () {
        return function () {
            if (ko.unwrap(allBindings().enable) ||
                (allBindings().disable == null || !ko.unwrap(allBindings().disable))) {
                valueAccessorOrig.apply(this, arguments);
            }
        }
    };

    // apply wrapped click to original click init
    koClickInitOrig.apply(this, arguments);
};

不太清楚在没有单击处理程序的情况下启用/禁用的用例,哈哈。这是一个支持禁用的版本:http://jsfiddle.net/xCfQC/31/。 - Anders
1
@Anders 当你有一个设置了href属性且不需要点击的锚点时。那就是我的情况。 - Edyn
但是 href 仍然会执行,这只是 CSS?您需要从单击处理程序返回 false 才能防止其触发,您可以在包装器 click 处理程序中修复它。 - Anders
@Anders 你说得对... 添加了处理那个的代码。能够做到不需要使用 knockout 的 click 处理程序。 - Edyn

0
这是我的方法:
JavaScript
(function () {
  var originalDisableUpdate = ko.bindingHandlers.disable.update;

  ko.bindingHandlers.disable.update = function (element, valueAccessor) {
      if (element.tagName === 'A') {
        var
          value = ko.utils.unwrapObservable(valueAccessor()),
          disabled = 'disabled';

        if (value) {
          element.setAttribute(disabled, null);
        }
        else {
          element.removeAttribute(disabled);
        }
      }
      else {
        originalDisableUpdate(element, valueAccessor);
      }
  };
})();

CSS

a[disabled] {
  pointer-events:none;
  cursor:default;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接