3

Is it possible to extend the text binding in knockout with additional options? For example:

ko.bindingHandlers.text = {
    update: function(element, valueAccessor, allBindingsAccessor){
        var options = allBindingsAccessor().textOptions || {};
        options = _.extend({
            'required': false
        }, options);
        if(options['required']){
            // Do some required things
        }
    }
};

So then a text binding could be written as:

<span data-bind="text: myText, textOptions: { required: true }" />

Is this possible or would a custom binding be required?

Jeroen
  • 60,696
  • 40
  • 206
  • 339
Brian Triplett
  • 3,462
  • 6
  • 35
  • 61
  • Beware that `span` is *not* allowed to be a self-closing element. Weird things in some browsers may happen if you don't create a regular open+close. – Jeroen Aug 25 '15 at 21:21

2 Answers2

4

For wrapping existing bindings, see section 3 here. The gist is that in the init section, you call ko.bindingHandlers.text.init and similarly for update. Around those calls, you can do anything else you like. If the binding you're wrapping doesn't have one of init or update, you'll get an error, and you can just remove that call.

Roy J
  • 42,522
  • 10
  • 78
  • 102
  • Ah yesss of course, +1, that's a good option too. [I've even suggested the same in the past](http://stackoverflow.com/a/18124635/419956) for a scenario with the `html` binding. Kudos! – Jeroen Aug 25 '15 at 21:23
2

Hmm, that's not extending the text binding, you're overwriting it.

First things first, note that currently the text binding is extremely simple. Taken from the source:

ko.bindingHandlers['text'] = {
    'init': function() {
        // Prevent binding on the dynamically-injected text node (as developers are unlikely to expect that, and it has security implications).
        // It should also make things faster, as we no longer have to consider whether the text node might be bindable.
        return { 'controlsDescendantBindings': true };
    },
    'update': function (element, valueAccessor) {
        ko.utils.setTextContent(element, valueAccessor());
    }
};

The actual functionality is offloaded to setTextContent, which handles cross-browser setting of, well, text content.

If you want to have a binding that does things similar to text, only with some added features, the easiest way probably is to create a custom binding handler yourself that utilizes setTextContent. For example:

ko.bindingHandlers['myText'] = {
    'init': function() {
        return { 'controlsDescendantBindings': true };
    },
    'update': function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()), txt = '';
        if (value !== null && typeof value == "object") {
            txt = value['text'];
            if (!!value['required']) { txt += "!!!"; }
          
        } else {
            txt = value;
        }
        ko.utils.setTextContent(element, txt);
    }
};

ko.applyBindings({ testProperty: "test text 1234" });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>

<span data-bind="myText: { text: testProperty, required: true }"></span>
<br>
<span data-bind="myText: { text: testProperty, required: false }"></span>
<br>
<span data-bind="myText: testProperty"></span>
Jeroen
  • 60,696
  • 40
  • 206
  • 339