Search code examples
knockout.jsknockout-3.0

Checked radio within label doesn't trigger event


I am trying to get the checked value from a radio button list and for this I am trying to load the checked value into an observable. From the documenation this is the suggested way:

<input type="radio" name="type"  data-bind="value:ct.value, checked: $root.selectedtype" required>

However, this doesn't work in my case. I have a label wrapped around my radio and if I add the click event on the label it populates the observable selectedtype correctly and in this way I can get the selected value such as:

 <label class="btn btn-outline-dark" data-bind="click:$root.selectedtype">

If the radio button gets selected why the 'checked' event won't trigger the desired action?

 <label for="email">Company Type</label>
                    <div data-toggle="buttons" class="form-group label-block">
                        <div class="row">
                            <div class="col-sm-6" data-bind="foreach: { data: companytype, as: 'ct' }">
//-->   this way it works       <label class="btn btn-outline-dark" data-bind="click:$root.selectedtype"> 
                                    <input type="radio" name="type"  data-bind="value:ct.value" required>
//-->   this way it does not work          <input type="radio" name="type"  data-bind="value:ct.value, checked: $root.selectedtype" required>
                                    <span data-bind="text: ct.label"></span>
                                </label>
                            </div>
                        </div>
                        <div class="help-block with-errors"></div>
                    </div>

 Selected: <span data-bind="text: selectedtype().value"></span>-

In my model:

   var companytype = ko.observableArray([]);
   var selectedtype = ko.observable('');

   var init = function () {
        getItemList();
        companytype.push({ "label": "LTD", "value": "1" });
        companytype.push({ "label": "PLC", "value": "2" });        
        companytype.push({ "label": "LLP", "value": "3" });
        companytype.push({ "label": "Sole Trader", "value": "4" });
    }

Solution

  • var companytype = ko.observableArray([]);
    var selectedtype = ko.observable('');
    
    var init = function() {
      //getItemList();
      companytype([
        { "label": "LTD", "value": "1" },
        { "label": "PLC", "value": "2" },
        { "label": "LLP", "value": "3" },
        { "label": "Sole Trader", "value": "4" }
      ]);
    }
    
    init();
    var viewModel = { companytype, selectedtype };
    
    ko.applyBindings(viewModel);
    .btn {
      border: 1px solid black;
      padding: 2px 4px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    
    <label for="email">Company Type</label>
    <div data-toggle="buttons" class="form-group label-block">
      <div class="row">
        <div class="col-sm-6" data-bind="foreach: { data: companytype, as: 'ct' }">
          <label class="btn btn-outline-dark" data-bind="click: function(){	$root.selectedtype($data); return true; }"> 
              <input type="radio" name="type"  data-bind="checked: $root.selectedType" required>
              <span data-bind="text: ct.label"></span>
          </label>
        </div>
      </div>
      <div class="help-block with-errors"></div>
    </div>
    
    Selected: <span data-bind="text: ko.toJSON(selectedtype)"></span>

    You can use the checked binding, but because the label and the radio button control are both trying to handle the click event they fight each other. You need to have the label's click function return "true" so that the event does not continue down to the radio control.

    <label class="btn btn-outline-dark" data-bind="click: function(){ $root.selectedtype($data); return true; }"> 
        <input type="radio" name="type" data-bind="checked: $root.selectedType" required>
        <span data-bind="text: ct.label"></span>
    </label>