26

What's the suggested "best practice" way to use Knockout's "attr" data binding with standalone attributes like "readonly" and "disabled"?

These attributes are special in that they are generally enabled by setting the attribute value to the attribute name (although many browsers work fine if you simply include the attribute names without any values in the HTML):

<input type="text" readonly="readonly" disabled="disabled" value="foo" />

However, if you don't want these attributes to be applied, the general practice is to simply omit them altogether from the HTML (as opposed to doing something like readonly="false"):

<input type="text" value="foo" />

Knockout's "attr" data binding doesn't support this scenario. As soon as I provide an attribute name, I need to provide a value as well:

<input type="text" data-bind="attr: { 'disabled': getDisabledState() }" />

Is there a cross-browser way turn off 'disabled' or 'readonly'? Or is there a trick with a custom binding that I can use to not render anything if I don't want the item disabled or made read-only?

Armchair Bronco
  • 2,367
  • 4
  • 31
  • 44
  • I don't understand, why you need to provide disabled if you don't disabled to even show? – jjperezaguinaga Jan 04 '13 at 21:32
  • The example I gave is designed to simply demonstrate the problem. The issue is this: some attributes in HTML are standalone attributes - they don't really require a value. And if you don't want these attributes to affect the HTML, then you simply omit them. But Knockout's "attr" data-binding mechanism doesn't support this scenario. – Armchair Bronco Jan 04 '13 at 21:34

3 Answers3

41

Knockout's "attr" data binding does support this scenario just return null or undefined from your getDisabledState() function then it won't emit the attribute.

Demo Fiddle.

nemesv
  • 138,284
  • 16
  • 416
  • 359
  • How can I verify that the attribute hasn't been emitted using Firebug or a similar tool? When I try to view the "live" HTML from the demo Fiddle above, I'm still seeing the Knockout data-binding code, not the live, rendered tags. (However, I'll admit that it never occurred to me to do this; in my getDisabledState() function, I was always returning either "disabled" or the empty string ""). – Armchair Bronco Jan 04 '13 at 21:47
  • If my fiddle working with your browser then it is working. I don't use Firebug. In the Chrome dev tools it is clear that it adds and removes the attribute if you return `"disabled"` and `undefined`. – nemesv Jan 04 '13 at 21:50
  • OK. I have verified this using Chrome. Thanks for the quick response and the demo Fiddle. It's a simple and elegant solution that never occurred to me. I typically initialize my return values to the expected data type (which for 'disabled' or 'readonly' is a string). I'll just initialize to 'undefined' and I should be good to go. Thanks! – Armchair Bronco Jan 04 '13 at 21:55
  • 3
    If you getDisabledState() function return true/false, you can use ternary operator inline, without modifying your function: I will have the same effect as returning string or null. – Jacob Jedryszek Oct 02 '14 at 00:34
9

You can also create a binding for readonly like this:

ko.bindingHandlers['readonly'] = {
'update': function (element, valueAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor());
    if (!value && element.readOnly)
        element.readOnly = false;
    else if (value && !element.readOnly)
        element.readOnly = true;
}
};

Source: https://github.com/knockout/knockout/issues/1100

Greg Gum
  • 33,478
  • 39
  • 162
  • 233
  • Thanks for sharing the source, and including the code inline. – John Zabroski Jun 15 '16 at 12:37
  • Great post! Could you also include a demo of how to use it? I'm sure I could figure it out, but I've never made a custom binding before, and it would be useful to others as well. – jpaugh Apr 24 '18 at 19:59
  • @jpaugh, Sorry, I haven't used KO in several years. These days, I use the Aurelia framework. – Greg Gum Apr 24 '18 at 22:52
7

Knockout has an enable binding as well as a disable binding.

I'm not sure if these were available when the question was asked, but anyone referring back to this issue should be aware.

  • 1
    Be sure to pick the right one. `!observableProperty` does not work. The NOT `!` is not picked up by the evaluator. – P.Brian.Mackey Apr 30 '14 at 21:07
  • 4
    !observableProperty() works though :) much faster and often cleaner than a custom binding – mikus Sep 24 '14 at 13:57
  • 1
    @P.Brian.Mackey That is a common Knockout beginner mistake. When you add an operator like ! in front of an observableProperty, you need to explicitly call it by adding () at the end of the propertyName. This tells Knockout to lift the whole expression to a synthetic computed. If you use Chrome Debugger and type in a bogus observableProperty that doesn't exist, it will generate an error with a VM:SomeLineNumber in the Console. Click the VM:SomeLineNumber and you will see what Knockout generated. – John Zabroski Jun 15 '16 at 12:22