0

I have not been able to find a solution for this.

I'm trying to create a custom bindingHandler for a click event on an html element div tag.

// custom bindingHandler and observable
<div data-bind="OnClickEvent: HasBeenClicked"></div>
// to show observable's true/false value
<span data-bind="text: 'Observable: ' + HasBeenClicked()"></span>

This bindingHandler does what it is suppose to, if you click on the div the text will change. However, the observable says if false.

ko.bindingHandlers.OnClickEvent = {
    update: function (element, valueAccessor) {

        $(element).text('Click Here: false');

        var observable = ko.utils.unwrapObservable(valueAccessor());

        $(element).on('click', function () {

            observable = !observable;

            if (observable) {
                $(element).text('Click Here: true');
            } else {
                $(element).text('Click Here: false');
            }
        });
    }
}


function ViewModel() {
    var self = this;
    self.HasBeenClicked = ko.observable(false);
}

ko.applyBindings(new ViewModel());

How do I set the observable HasBeenClicked to switch between true/false on every click, like the text?

shammelburg
  • 6,974
  • 7
  • 26
  • 34
  • 1
    You are not writing back the new value to your observable. So you need to `valueAccessor()` to do that: `observable = !observable; valueAccessor()(observable);`. However you custom binding is very strange and buggy. For example for each observable change it will again and again subscribe on the click event... What do you want to achieve with this binding? – nemesv Jun 01 '15 at 10:45
  • I know its a strange one, it is to see how I can change the observable on click. Eventually I will use the bootstrap glyphicons to make it look like a fancy checkbox. However, adding the bit of code you suggested does not change the observable. thanks @nemesv – shammelburg Jun 01 '15 at 10:57

1 Answers1

2

I second @nemesv's comment on the quick fix as well as the fact that your binding looks a bit strange. I'll expand it to an answer because I need the room.

First up, the quick fix:

  • I think for the type of binding you're writing it's more appropriate to do your work inside the init part;
  • You need to write back to the valueAccessor to change the observable's value (I show a quick and dirty demo below);

Here's a working example:

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

        $(element).text('Click Here: false');

        var observable = ko.utils.unwrapObservable(valueAccessor());
        
        $(element).on('click', function () {
            observable = !observable;
            
            valueAccessor()(observable);

            if (observable) {
                $(element).text('Click Here: true');
            } else {
                $(element).text('Click Here: false');
            }
        });
    }
}

function ViewModel() {
    var self = this;
    self.HasBeenClicked = ko.observable(false);
}

ko.applyBindings(new ViewModel());
div { padding: 10px; background-color: pink; margin-bottom: 10px; cursor: pointer; }
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

<div data-bind="OnClickEvent: HasBeenClicked"></div>
<span data-bind="text: 'Observable: ' + HasBeenClicked()"></span>

However, I think this kind of mix of jQuery, event handling, and Knockout is rather iffy. You should consider whether you're suffering from an XY-problem, and whether you can't solve your requirement using existing binding handlers. Here's one way to do something similar:

function ViewModel() {
    var self = this;
    self.HasBeenClicked = ko.observable(false);
    self.handleClick = function() {
      self.HasBeenClicked(!self.HasBeenClicked());
    };
}

ko.applyBindings(new ViewModel());
div { padding: 10px; background-color: pink; margin-bottom: 10px; cursor: pointer; }
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

<div data-bind="click: handleClick, text: 'Click here: ' + HasBeenClicked()"></div>
<span data-bind="text: 'Observable: ' + HasBeenClicked()"></span>

Speaking of existing binding handlers, know that there's the click binding which may suit your needs. If it doesn't, I'd recommend looking at the relevant source (click redirects to the event binding, see this file in GitHub), to draw inspiration for your own handler.

Jeroen
  • 60,696
  • 40
  • 206
  • 339
  • Thanks @Jeroen & nemesv, I know it's a strange one but I was curious about the way knockout would set the observable value. – shammelburg Jun 01 '15 at 11:55