简体   繁体   中英

Knockout checked binding not working

I am using the Twitter Bootstrap button group along with Knockout. I feel like I am overlooking something very simple, but, I haven't been able to get the checked binding working in this instance.

I have a jsFiddle reproducing the problem here: http://jsfiddle.net/n5SBa/ .

Here is the code from the fiddle:

HTML

<div class="form-group">
    <div class="btn-group" data-toggle="buttons">
        <label class="btn btn-primary" data-bind="click: ClickScore.bind($data, '0'), css: { active: Score() == '0' }">
            <input type="radio" name="score" value="0" data-bind="checked: Score" /> 0
        </label>
        <label class="btn btn-primary" data-bind="click: ClickScore.bind($data, '1'), css: { active: Score() == '1' }">
            <input type="radio" name="score" value="1" data-bind="checked: Score" /> 1
        </label>
        <label class="btn btn-primary" data-bind="click: ClickScore.bind($data, '2'), css: { active: Score() == '2' }">
            <input type="radio" name="score" value="2" data-bind="checked: Score" /> 2
        </label>
        <label class="btn btn-primary" data-bind="click: ClickScore.bind($data, '3'), css: { active: Score() == '3' }">
            <input type="radio" name="score" value="3" data-bind="checked: Score" /> 3
        </label>
        <label class="btn btn-primary" data-bind="click: ClickScore.bind($data, '4'), css: { active: Score() == '4' }">
            <input type="radio" name="score" value="4" data-bind="checked: Score" /> 4
        </label>
        <label class="btn btn-primary" data-bind="click: ClickScore.bind($data, '5'), css: { active: Score() == '5' }">
            <input type="radio" name="score" value="5" data-bind="checked: Score" /> 5
        </label>
    </div>
</div>

<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>

Javascript

function SurveyViewModel() {
    var self = this;

    self.Score = ko.observable();

    //Events
    self.ClickScore = function (score) {
        self.Score(score);
    };

    //Computations
    self.RecommendationLabel = ko.computed(function () {
        if (self.Score() < 8) {
            return "Some question?";
        } else {
            return "Some other question?";
        }
    });

    self.DOMSelectedScore = ko.computed(function() {
        if ($('input[name=score]:checked').val()) {
            return $('input[name=score]:checked').val();
        } else {
            return 'no value!';   
        }
    });
};

var surveyViewModel = new SurveyViewModel();

ko.applyBindings(surveyViewModel);

In the example, I'm unable to get the actual radio button selected in the DOM so that it can be properly submitted in my form.

The values of the checkboxes are strings ( "0" , "1" , etc.), but you are setting the value of the observable to a number. The checked binding uses strict equality when doing the comparison and doesn't consider the number 1 to equal the string "1" .

You can fix this by setting the value of the observable to strings:

data-bind="click: ClickScore.bind($data, '1')"

http://jsfiddle.net/mbest/n5SBa/2/

The DOMSelectedScore computed observable doesn't reference any other observable, so it never gets recalculated.

self.DOMSelectedScore = ko.computed(function() {
    self.Score(); // subscribe to Score, even if we don't use it.
    var val = $('input[name=score]:checked').val();
    return val || 'no value!';
});

Fixing that, it appears that the DOM isn't updated until after the function returns, so the value lags behind one click. To fix this, we need to delay until the DOM has been updated:

self.DOMSelectedScore = ko.computed(function() {
    self.Score(); // subscribe to Score, even if we don't use it.
    var val = $('input[name=score]:checked').val();
    return val || 'no value!';
}).extend({throttle:1}); // Wait for the DOM update to complete

http://jsfiddle.net/n5SBa/5/


To simplify the binding of numbers with knockout, you could define an extension method for observables:

ko.observable.fn.asString = function () {
    var source = this;
    if (!source._asString) {
        source._asString = ko.computed({
            read: function () {
                return String(source());
            },
            write: function (newValue) {
                source(Number(newValue));
            }
        });
    }
    return source._asString;
};

And then

<input type="radio" name="score" value="2" data-bind="checked: Score.asString()"/>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM