knockout icon indicating copy to clipboard operation
knockout copied to clipboard

Radio buttons with number values not initialising selection

Open jhsowter opened this issue 12 years ago • 4 comments

http://jsfiddle.net/jhsowter/TX4ta/

In the above fiddle, the value is initialised in the viewModel, but the appropriate radio button is not selected. If you change the knockout version back to 2.3.0 (or anything else) it behaves as expected.

The problem doesn't occur if the values of the radio buttons are strings as below. http://jsfiddle.net/jhsowter/7L3xW/

jhsowter avatar Nov 22 '13 14:11 jhsowter

Further investigation revealed that if you set the initial value in the model to a string representation of 2, the value gets set correctly. So it seems likely that the code internally does comparison using the stricter === rather than ==.

jhsowter avatar Nov 22 '13 15:11 jhsowter

I couldn't find another bug report for this issue so I will post this here. I ran into a similar problem where my ViewModel classes are populated from server JSON which contain numeric Enum values. It would be too much work to massage the server result into strings and using checkedValue binding for everything doesn't seem right.

Here is the solution I found, works with 3.4.0. Insert this at line 4085 if (!(allBindings['has']('checkedValue') || allBindings['has']('value')) && typeof modelValue !== 'string') if (modelValue === null || modelValue === undefined) modelValue = ''; else if(typeof modelValue.toString === 'function') modelValue = modelValue.toString();

I hope the team can add support for this in future releases.

Azarem avatar Sep 06 '16 16:09 Azarem

This is the way that it is documented to work. If you are using a non-string value, then use the checkedValue binding as well. At least thats what it says in the docs.

gregveres avatar Feb 13 '17 19:02 gregveres

Location of the code change from https://github.com/knockout/knockout/issues/1217#issuecomment-245014232 is in this function

        function updateView() {
          // This updates the view value from the model value.
          // It runs in response to changes in the bound (checked) value.
          var modelValue = ko.utils.unwrapObservable(valueAccessor()),
            elemValue = checkedValue();

          if (valueIsArray) {
            // When a checkbox is bound to an array, being checked represents its value being present in that array
            element.checked = ko.utils.arrayIndexOf(modelValue, elemValue) >= 0;
            oldElemValue = elemValue;
          } else if (isCheckbox && elemValue === undefined) {
            // When a checkbox is bound to any other value (not an array) and "checkedValue" is not defined,
            // being checked represents the value being trueish
            element.checked = !!modelValue;
          } else {
            // Otherwise, being checked means that the checkbox or radio button's value corresponds to the model value
            element.checked = (checkedValue() === modelValue);
          }
        };

New function would look like this I believe.

         function updateView() {
          // This updates the view value from the model value.
          // It runs in response to changes in the bound (checked) value.
          var modelValue = ko.utils.unwrapObservable(valueAccessor()),
            elemValue = checkedValue();

          if (valueIsArray) {
            // When a checkbox is bound to an array, being checked represents its value being present in that array
            element.checked = ko.utils.arrayIndexOf(modelValue, elemValue) >= 0;
            oldElemValue = elemValue;
          } else if (isCheckbox && elemValue === undefined) {
            // When a checkbox is bound to any other value (not an array) and "checkedValue" is not defined,
            // being checked represents the value being trueish
            element.checked = !!modelValue;
          } else {

              if (!(allBindings['has']('checkedValue') || allBindings['has']('value')) && typeof modelValue !== 'string') {
                if (modelValue === null || modelValue === undefined) {
                  modelValue = '';
                }
                else if (typeof modelValue.toString === 'function') {
                  modelValue = modelValue.toString();
                }
              }

            // Otherwise, being checked means that the checkbox or radio button's value corresponds to the model value
            element.checked = (checkedValue() === modelValue);
          }
        };

It seems like another way to fix this would be to use a == instead of a ===. This change was made with the jump to v3.0 https://github.com/knockout/knockout/commit/9eacf842543b8e035ac907f0f437f730a1d11a8e or here https://github.com/knockout/knockout/commit/fac900a973bcac3fa58d2d24299d9c31987e5a39

mikecarper avatar Oct 02 '24 19:10 mikecarper

I am new to KO, but this topic just seems like a bug that needs to be fixed. Given that you can't set a value in HTML as an "integer" per se, then it is only fair that an integer in the ViewModel would match the value in the radio's value property.

I don't understand @gregveres point. The documentation is confusing at best in the context of radios. Here is an example of using checkedValue https://jsfiddle.net/1qetrcsu/ Maybe I am off the mark.

daveapsgithub avatar Jul 07 '25 14:07 daveapsgithub

@daveapsgithub I would not recommend being "new to KO" at this point. I don't believe that KO is maintained anymore and there are much better systems to use. I would highly recommend looking at Vue.

Vue has a reactivity system that is very similar to KO's observables but works better, IMO. Also, when I switched from KO to Vue, my code got so much better because of Vue's natural support for components. I had a few components in KO, but given how clunky they are to use, I rarely used them. But with Vue, components are easy to define and use, I now have over 700 of them in my system and it makes it so much easier to work on and maintain.

gregveres avatar Jul 07 '25 14:07 gregveres