Question

How can i change value of observable inside of subscribe function? For example

JS Model:

function Model(){
    self = this;
    self.Test = ko.observable(2);
    self.Test.subscribe(function (){
      if (/**Some bool expression**/) {
         self.Test(2);
      }
    });
}

HTML:

<input type="radio" value="1" data-bind="checked:Test" />
<input type="radio" value="2" data-bind="checked:Test" />
<input type="radio" value="3" data-bind="checked:Test" />

By default second radio input is checked. After I clicked on first radio, both first and second is selected.

enter image description here

UPDATED: It is happens when I include both jQuery and knockout. If remove jquery then all is Ok. But it is just test page. In real project I need to use jQuery in some places, and in this case I can not remove it.

Sample. Source of test page:

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script type="text/javascript" src="knockout-2.1.0.js"></script>
        <script type="text/javascript" src="jquery-1.6.4.js"></script>
        <script type="text/javascript">
            function Model(){
                self = this;
                self.Test = ko.observable(2);
                self.Test.subscribe(function (){
                  if (true) {
                     self.Test(2);
                  }
                });
            }
        </script>
    </head>
<body>
    <input type="radio" value="1" data-bind="checked:Test" />
    <input type="radio" value="2" data-bind="checked:Test" />
    <input type="radio" value="3" data-bind="checked:Test" />
</body>
<script type='text/javascript'>
    ko.applyBindings(new Model());
</script>
</html>
Was it helpful?

Solution

UPDATE (2015-02-27): As of Knockout 3.1.0, Knockout doesn't include any "workaround" code for the click event and should have no problem handling the example in the question. http://jsfiddle.net/9S96U/3/


When jQuery is included, Knockout uses it to handle events, including the click event used to respond to radio buttons. It includes some "workaround" code to make sure the click handler sees the correct checked state of the radio button, but this seems to interfere with your code that tries to reset the checked value in the middle.

A solution is to update the value using setTimeout. That way it happens after the click handler has finished.

function Model(){
    var self = this;
    self.Test = ko.observable(2);
    self.Test.subscribe(function (){
        setTimeout(function() {
            self.Test(2);
        });
    });
}

Example: http://jsfiddle.net/9S96U/1/

Also you need to include var before self = this so that you're not overwriting window.self.

OTHER TIPS

The idea behind subscribing/publishing is that you can have one observer send the result to multiple subscribers, who do their own testing.

I haven't actually used knockout.js, yet, but I've done a LOT of work with delegation/async flow/promises/etc -- mostly using lightweight custom implementations for special-cases.

Your goal is either to have a controller which runs the test once (what you've got now), and then iterates through each one, making sure that the right one (i - 1) is checked, and all of the others are unchecked...

...or in a more-traditional publisher/subscriber fashion, to have one publisher (like the fieldset which might be the parent element to all of the radio controls) listen for one of the inputs to change, and then notify EACH of the subscribing inputs of the value, and let them do their own testing.

In pseudo-speak:

var subscribers = []; // array of input "Model" callbacks
                      // (ie: tests to determine whether they're off or on)

parentElement.listen("click", function (e) {
        var self = this,
            selectedRadio = e.target,
            val = selectedRadio.value;

        subscribers.forEach(function (notify) { notify(val); });
});

To handle your current situation, you could use dependency-injection:

new Model( value );

or

new Model( subscription_test_for_element );

where you cache either the expected test-value, or where you cache your input-specific test/success/failure logic inside of each individual instance...

...or you create a larger controller (not recommended) for handling the hardcoded-logic for each input.

Of course, more-specific help could be given based on exactly what your model is, how you're instantiating, why you're hard-coding your subscription into the construction if it's going to be a generic model, et cetera.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top