Cross posting this from Stack Overflow:
http://stackoverflow.com/questions/10283580/binding-knockoutjs-to-radio-button-with-computed
I have a question with an array of answers. Each answer has a boolean
property called isRight, which represents whether the answer
is...right.
I'm trying to render a radio button for each answer, and have it be
checked if that answer is right. If the user clicks a different
button, that answer becomes correct.
I understand that KO binds the checked property to a value, and each
radio button will only be checked if the radio's value matches the
bound value; you can't just bind directly to isRight.
I put a computedObservable on the question itself, which should do all
that. And it works. The problem is, I want to subscribe to the change
event of the radio button, to see when a new answer is selected (and
send an ajax request). The problem I'm having is that the ko binding
is not updated in the change handler. It seems putting a delay in the
handler will give me what I want, but I'd prefer a more direct
solution.
And it seems the click event works perfectly, but that'll fire each
time a radio button is clicked, even if it's already checked.
Is there any way to ensure that the binding is updated in the change
event? Current code is ko 2.0, and I've also tried 2.1.
Full Fiddle:
http://jsfiddle.net/PC4aF/1/
Source:
function Question() {
this.name = "My Question";
var i = 0;
this.answers = ko.observableArray([
new Answer(++i, "Answer 1", false),
new Answer(++i, "Answer 2", true),
new Answer(++i, "Answer 3", false)]);
this.correctAnswer = ko.computed({
read: function () {
for (var i = 0, max = this.answers().length; i < max; i++)
if (this.answers()[i].isRight())
return "answer-" + this.answers()[i].id();
},
write: function (newValue) {
var newId = +newValue.split('-')[1];
for (var i = 0, max = this.answers().length; i < max; i++)
this.answers()[i].isRight(this.answers()[i].id() ===
newId);
},
owner: this
});
}
function Answer(id, name, isRight) {
this.id = ko.observable(id);
this.name = ko.observable(name);
this.isRight = ko.observable(isRight);
}
$(function () {
ko.applyBindings(new Question());
$(document).on("change", "input[type='radio']", function () {
var answer = ko.dataFor(this);
var isRight = answer.isRight();
setTimeout(function () { alert("before = " + isRight + "
after = " + answer.isRight()); }, 1000);
});
});
HTML
<div data-bind="text:name"></div>
<div data-bind="foreach:answers">
<label>
<span data-bind="text: name"></span>
<input type="radio" name="uniqueQuestionName" data-
bind="value: 'answer-' + id(), checked:$parent.correctAnswer" />
</label>
<br />
</div>