How can i change value of observable inside of subscribe function?
Asked Answered
T

2

5

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>
Tribble answered 24/10, 2012 at 19:15 Comment(2)
Maybe you can reproduce based on this fiddle: jsfiddle.net/rniemeyer/2uCAF? I think that you might have another issue going on, because the binding would not let you select both the first and second radios. You should also give your inputs a common name attribute, which will keep them grouped, although KO will still keep them in sync anyways.Gramme
Yes, add the name attribute. w3.org/TR/html401/interact/forms.html#radioMichelemichelina
E
7

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.

Erickson answered 24/10, 2012 at 22:41 Comment(0)
B
0

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.

Bron answered 24/10, 2012 at 21:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.