Disable anchor tag in knockout.js

2019-04-18 15:42发布

问题:

I need to disable the anchor tag inside a foreach loop of knockout.js in HTML.

Here is my code:

<a id="aQStreamSkype" data-bind="attr:{href: ''}, click: $parent.StoreUserClick,disable: ($data.SkypeId == 'null')">Skype </a>

回答1:

Anchor tags cannot be disabled.
The easiest is to use ko if binding and then render a span instead of the anchor if the skype id is null

<!-- ko if: skypeId === null -->
    <span >No Skype Id</span>
<!-- /ko -->
<!-- ko if: skypeId !== null -->
    <a id="aQStreamSkype" data-bind="attr:{href: ''}, click: $parent.StoreUserClick,text: skypeId"></a>
<!-- /ko -->

Here is a fiddle



回答2:

If there is no href attribute on a element but only an action in a click binding, then an easy way would be passing expression condition && handler to a click binding.

If condition is observable you'll need to add parentheses.

<a data-bind="click: flag1() && handler">Enabled link</a>
<a data-bind="click: flag2() && handler">Disabled link</a>

It will be evaluated as false if condition is false (so nothing will happen),
and will be evaluated as a handler if condition is true.

Fiddle here



回答3:

Disable only works with form elements, not anchor tags. You could use the visible binding instead, and just hide the link if there is no user id. If you do want to show something even if there isn't a user id, then add a span with the opposite visible test, then one will be shown if there is a user id, and the other if there isn't:

<a id="aQStreamSkype" data-bind="attr:{href: ''}, click: $parent.StoreUserClick, visible: ($data.SkypeId !== 'null')">Skype </a>
<span class="notLink" data-bind="visible: ($data.SkypeId === 'null')">Skype </span>

As a side note, if SkypeId is an observable, you will need to call it as one in your comparison check:

($data.SkypeId() !== 'null')


回答4:

With some override magic you can get this behaviour without that your view or ViewModel code need changes

  (function () {
      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);
      };
  })();

When you include that code the enable binding will work for anhors

Fiddle, it uses my convention library so ignore that part http://jsfiddle.net/xCfQC/4/



回答5:

First off, there's a school of thought with which I have some sympathy that says just don't do it. A hyperlink that does nothing is just text.

However, I can see the flip side of the coin - I came across this same problem because I am using a Bootstrap dropdown menu which contains anchor tags as menu options, and to my mind it gives a better user experience to show the menu options as disabled rather than just not show them at all, and I feel it's cleaner to 'disable' the hyperlink than to include markup for a hyperlink and a span with the same text and then conditionally show one or the other. So, my solution was:

<a data-bind="click: checkCondition() ? myFunction : null, css: { disabled: !checkCondition() }"></a>

Note that checkCondition() is a function which returns true or false, indcating whether the hyperlink should be enabled or not. The css binding is just styling the hyperlink to appear disabled - you may have to add your own .disabled CSS class if you're not using Bootstrap.

The key to this working is that the anchor has no href attribute, so it's useless as a hyperlink without the Knockout click binding, which means that you could just as easily use this method with any element type (e.g. this could be a clickable span as easily as an anchor). However, I wanted to use an anchor so that my styling continued to be applied without any extra work.



回答6:

Knockout enable/disable binding do not support anchor tags.

So you have 2 solution to this.

Solution 1

<a href='#' title="Skype" data-bind='click: function() { 
 if(($data.SkypeId !== 'null'))
 {
    //call the desired method from here
 }' >

Solution 2

This button displays only when your condition is success and it has click binding

<a data-bind="click: $parent.StoreUserClick, visible: ($data.SkypeId != 'null')" href="#" title="Skype">

This button displays only when your negative condition is success and it do not have click binding

<a data-bind="visible: ($data.SkypeId == 'null')" href="#" title="Skype ">


回答7:

I found ko.plus an excellent library which implements command pattern. The 'action' can not be executed until 'canExecute' condition is true.

var vm = {
    enabled: ko.observable(false),
    StoreUserClick: ko.command({
        action: function () {
            window.alert('Command is active')
        },
        canExecute: function () {
            return vm.enabled();
        }
    })
}
ko.applyBindings(vm);
a.disabled {
    color: gray;
    text-decoration: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://raw.githubusercontent.com/stevegreatrex/ko.plus/master/dist/ko.plus.js"></script>

<a href="" id="aQStreamSkype" data-bind="click: StoreUserClick, css: { disabled: !StoreUserClick.canExecute() }">Skype</a>
<br />
<br />
<input type="checkbox" data-bind="checked: enabled">enabled



回答8:

Another option that I like to use is to disable the anchor using the "css" directive:

<a id="aQStreamSkype" data-bind="css: { disabled: $data.SkypeId == 'null' }">Skype</a>