简体   繁体   中英

Knockout.js : radio button first click does not show in 3.0, shows in 2.1-2.3

Fiddle : http://jsfiddle.net/pr5YQ/7/

I'm having what appears to be a relatively common problem with Knockout.js - whenever a radio button is first selected, the value in the object model updates, but the radio button itself does not show the selection. Upon second selection, the item shows the check.

The interesting thing is that Knockout version 2 works correctly! It's only Knockout version 3 that has an issue. Within the fiddle, switch to any version of Knockout 2 and the form works as needed.

I have checked to see if there are any issues with the data type I'm setting as the value (they're both numbers, which should be fine) and the name/value setup appears to be correct. I would expect that, if I had failed to do either of the above, then the first click failure would show in all versions of Knockout.

Here's a snippet of the data model I have :

        "Question_Ref": 1,
            "Question_Text": "Question 1",
            "Response_Ref": -1,
            "Response_Text": "",
            "Selected_Answer_Ref": -1,
            "Answers": [{
            "Answer_Ref": 1,
                "Answer_Text": "Yes",
                "Answer_Fails": false
        }, {
            "Answer_Ref": 2,
                "Answer_Text": "No",
                "Answer_Fails": false
        }]

And here's how I take the data object itself and attach the necessary KO bindings :

function AttachKOObjectsOnQuestion(question) {
    question.Selected_Answer_Ref_KO = ko.observable(question.Selected_Answer_Ref);
    question.Show_Response_KO = ko.computed({
        read: function () {
            if (!question.Selected_Answer_Ref_KO()) return null;
            if (question.Selected_Answer_Ref_KO() < 0) return null;
            for (selAnswerCount = 0; selAnswerCount < this.Answers.length; selAnswerCount++) {
                var answer = this.Answers[selAnswerCount];
                if (answer.Answer_Ref === this.Selected_Answer_Ref_KO()) {
                    return answer.Answer_Fails;
                }
            }
            return null
        },
        write: function (value) {},
        owner: question
    });
    var answerCount;
    for (answerCount = 0; answerCount < question.Answers.length; answerCount++) {
        AttachKOObjectsOnQuestionAnswer(question, question.Answers[answerCount]);
    }
}

function AttachKOObjectsOnQuestionAnswer(question, answer) {
    answer.ParentQuestion = question;
    answer.Selected_Answer_Ref_KO = ko.computed({
        read: function () {
            return answer.ParentQuestion.Selected_Answer_Ref_KO();
        },
        write: function (value) {
            var selectedAnswer = parseInt(value, 10);
            answer.ParentQuestion.Selected_Answer_Ref_KO(selectedAnswer);
        },
        owner: answer
    });
}

And here's the relevant setup of the radio buttons :

<table cellpadding="0" cellspacing="0">
    <tbody data-bind="foreach: Answers">
        <tr>
            <td>
                <input type="radio" data-bind="attr: { value: Answer_Ref, name: ParentQuestion.Question_Ref }, checked: Selected_Answer_Ref_KO, click: hideValidators" />
            </td>
            <td>
                <span data-bind="text: Answer_Text"></span>
            </td>
        </tr>
    </tbody>
</table>

This is both my first usage of Knockout and of JSON, so feel free to offer critiques. Thank you!

Your problem is that your checked binding is incorrect on the radiobutton. You're trying to bind checked to a property on your answer called Selected_Answer_Ref_KO . However, that you have added that object to the Question, not the Answer.

To fix this problem, just change your checked binding to checked: $parent.Selected_Answer_Ref_KO . I have updated your fiddle with this change and the selection work on the first click for me. You can find that update at http://jsfiddle.net/pr5YQ/5/ .

This was answered by Adam Rackis on Facebook.

Knockout 3 uses strict comparison, and I had not realized that anything in an input value is a string. As a result, the radio button value (a string) never matched my data model's value (a number).

Worse, I missed that the documentation mentions that you can use the checkedValue binding to set values that are not strings. That is the correct fix for my problem, as I want my data model's current type to remain a number.

<input type="radio" data-bind="attr: { name: ParentQuestion.Question_Ref }, checkedValue : Answer_Ref, checked: Selected_Answer_Ref_KO, click: hideValidators" />

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