20
votes

I have a bit of javascript:

function ViewModel() {
    var self = this;
    self.highlight = ko.observable(true);   
}

ko.applyBindings(new ViewModel());

And the html that complements it:

<div data-bind="css: { highlighted: highlight }, click: highlight( !highlight() )">
    random string
</div>

What I'm trying to achieve:

  1. The css class 'highlighted' to only be activated when var highlight is true
  2. Clicking on the div will toggle the bool value of var highlight
  3. Wanted result: clicking on the div to activate/deactivate its css class

What I'm getting:

  1. The initial value of highlight is true, yet the css class starts deactivated (if I change the initial value to false, the css class is activated: which seems as if I've somehow triggered the click bind when I haven't yet clicked anything)
  2. The div's css class does not toggle on click

I'd rather not make a new corresponding click function inside ViewModel. I'm looking if possible for a bit of code I can place solely inline within the data-bind.

Here's the code on JSFiddle: http://jsfiddle.net/4wt4x/1/

Can anyone explain what's happening and what I'm doing incorrectly?

4

4 Answers

21
votes

Your click: highlight( !highlight() ) is incorrect. Click is going to try to execute a function, and when the binding was initialized, highlight would have returned whatever its value was and that is what click is going to try to execute (true or false in your case). You need to do something like this:

In your javascript, place in your model:

self.toggleHighlight = function () { self.highlight(!self.highlight()) };

Then change the binding to say click: toggleHighlight

Like so: http://jsfiddle.net/KDypD/1/

You may need to adjust your highlight initial value as well to reflect how you want the page to show up initially.

43
votes

I know it's an old question, but maybe could help someone. If you need to use toggle in a lot of places, maybe some custom binding sugar could help you:

Binding:

ko.bindingHandlers.toggleClick = {
    init: function (element, valueAccessor) {
        var value = valueAccessor();

        ko.utils.registerEventHandler(element, "click", function () {
            value(!value());
        });
    }
};

Usage:

<div data-bind="css: { highlighted: highlight }, toggleClick: highlight">
    random string
</div>

Example:

http://jsfiddle.net/A28UD/1/

This approach keeps some of my views very cleaner. Hope it helps.

7
votes

Another option is to use a reusable custom function extension (custom function is used instead of extender because there are no parameters and it looks cleaner):

ko.observable.fn.toggleable = function () {
    var self = this;
    self.toggle = function () {
        self(!self());
    };

    return self;
};

Usage

self.highlight = ko.observable(true).toggleable(); 

Html

<div data-bind="css: { highlighted: highlight }, click: highlight.toggle">
    random string
</div>
4
votes

If you really want to do it inline:

<div data-bind="click: highlight.bind($root, !highlight()), css: { highlighted: highlight } ">
    random string
</div>

where highlight is the boolean observable.