Working with a list of checkboxes in knockoutjs

27,828

Solution 1

The checked binding expects to be passed a structure that it can read/write against. This could be a variable, an observable, or a writable dependentObservable.

When passed an array or observableArray, the checked binding does know how to add and remove simple values from the array.

Here is a sample that also includes a computed observable that contains the array as comma delimited values. http://jsfiddle.net/rniemeyer/Jm2Mh/

var viewModel = {
    choices: ["one", "two", "three", "four", "five"],
    selectedChoices: ko.observableArray(["two", "four"])
};

viewModel.selectedChoicesDelimited = ko.computed(function() {
    return this.selectedChoices().join(",");
}, viewModel);

ko.applyBindings(viewModel);

HTML:

<ul data-bind="template: { name: 'choiceTmpl', foreach: choices, templateOptions: { selections: selectedChoices } }"></ul>

<script id="choiceTmpl" type="text/html">
    <li>
        <input type="checkbox" data-bind="attr: { value: $data }, checked: $item.selections" />
        <span data-bind="text: $data"></span>
    </li>
</script>

Solution 2

Why isn't there a Mutually exclusive checkboxes example Online somewhere

Since this link came up first whilst I was searching for mutually exclusive checkboxes I will share my answer here. I was banging my head against the wall with all my attempts. By the way, when you handle the click event in a binding in-line knockoutjs it seems to disconnect the bindings(maybe only because I tried to call my resetIllnesses function as defined below) even if you return true from the function. Maybe there is a better way but until then follow my lead.

Here is the type I needed to bind.

var IllnessType = function (name,title) {
    this.Title = ko.observable(title);
    this.Name = ko.observable(name);
    this.IsSelected = ko.observable(false);
};

The array to bind with.

model.IllnessTypes = ko.observableArray(
    [new IllnessType('IsSkinDisorder', 'Skin Disorder'),
        new IllnessType('IsRespiratoryProblem', 'Respiratory Problem'),
        new IllnessType('IsPoisoning', 'Poisoning'),
        new IllnessType('IsHearingLoss', 'Hearing Loss'),
        new IllnessType('IsOtherIllness', 'All Other Illness')]
);

The reset illness function to clear them all.

model.resetIllnesses = function () {
    ko.utils.arrayForEach(model.IllnessTypes(), function (type) {
        type.IsSelected(false);
    });
};

The markup

<ul data-bind="foreach:IllnessTypes,visible: model.IsIllness()">
    <li><label data-bind="html: Title"></label></li>
    <li><input class="checkgroup2" type="checkbox" 
data-bind="attr:{name: Name },checked:IsSelected" /></li>
</ul>

This just doesn't work

If you have been struggling with trying to call the resetIllness function as I below, you will feel my pain.

<input type='checkbox' data-bind="checked:IsSelected, 
  click: function() { model.resetIllnesses(); return true; }" />

you have been sharing my pain. Well, it works! when you call it from following example. Notice that there is a class that I added above so that I can add the click function.

The script that makes all your problems go away.

<script type="text/javascript">
    $(function() {
        $(".checkgroup2").on('click', function() {
            model.resetIllnesses();
            var data = ko.dataFor(this);
            data.IsSelected(true);
        });
    });
</script>

Send info to the server

Also, in my case I had to send the information up to the server differently than the default html format so I changed the inputs a little.

<input class="checkgroup2" type="checkbox" data-bind="checked:IsSelected" />
<input type="hidden" data-bind="attr:{name: Name },value:IsSelected" />
Share:
27,828
bomortensen
Author by

bomortensen

Updated on January 13, 2020

Comments

  • bomortensen
    bomortensen over 4 years

    I'm trying to get my head around Knockout.js and I'm quite stuck when it comes to checkboxes.

    Server side I'm populating a set of checkboxes with their corresponding values. Now, when any of the unchecked checkboxes are checked, I need to store it's value in a comma-seperated string. When they're unchecked, the value needs to be deleted from the string.

    Have anyone got a hint on how to achieve this with knockoutjs?

    I have the following code so far:

    ViewModel:

    $().ready(function() {
       function classPreValue(preValue)
       {
           return {
                preValue : ko.observable(preValue)
           } 
       }
    
       var editOfferViewModel = {
       maxNumOfVisitors : ko.observable(""),
       goals : ko.observable(""),
       description : ko.observable(""),
       contact : ko.observable(""),
       comments : ko.observable(""),
       classPreValues : ko.observableArray([]),
       addPreValue : function(element) {
          alert($(element).val());
          this.classPreValues.push(new classPreValue(element.val()));
       }
     };
    
     ko.applyBindings(editOfferViewModel);
    });
    

    And my checkboxes are populated with a foreach loop:

    <input data-bind="checked: function() { editOfferViewModel.addPreValue(this) }"
           type="checkbox" checked="yes" value='@s'>
        @s
    </input>
    

    I try to pass the checkbox element as the parameter to my addPreValue() function, but nothing seems to happen when I check the checkbox?

    Any help/hints on this is greatly appreciated!

  • bomortensen
    bomortensen almost 13 years
    Niemeyer, thanks so much for your example! :) Greatly appreciated! I have one question though: what if I have some pre-populated checkboxes? How exactly do I go about fetching these into the array? Still new to knockout, so please bear with me ;) Thanks again!
  • RP Niemeyer
    RP Niemeyer almost 13 years
    Would this suffice: jsfiddle.net/rniemeyer/GErdL or do you have something more complicated in your scenario?
  • bomortensen
    bomortensen almost 13 years
    Hi again Niemeyer :) thanks for your fast replies! My scenario is that it's an edit-page which have textboxes and checkboxes. When entering the page, the selectboxes are populated from the server via a foreach loop. I might be slow, but how is it possible to grab the already-selected checkboxes values to knockoutjs? :) Thanks!
  • RP Niemeyer
    RP Niemeyer almost 13 years
    OK- out of the box, Knockout won't set the value based on its current state. Instead it will use whatever the view model says to determine what the state should be. However, it is easy to write a "wrapper" binding to the checked binding that will do this initialization for you. Here is a sample that would be closer to your server output: jsfiddle.net/rniemeyer/vgBUf . The checkedWithInit binding would do everything that the checked binding does, but would also make sure that the value is initialized as well.
  • bomortensen
    bomortensen almost 13 years
    Niemeyer, that's just awesome! There's no way I would ever figure that one out myself with one days experience with Knockout ;) It works like a charm, now I just have to take my time to understand what exactly is going on. So basically, with knockout you're able to write up your own bindinghandler - in this case to go through all the databound checkboxes and fetch their checked event and value (?) Thanks again, i'm so glad it works!
  • bomortensen
    bomortensen almost 13 years
    Oh, by the way, Niemeyer - i've been fooling around a bit with the script now to try and get an idea of what's going on. One thing I can't get my head around is: how can I store the text of the checkbox instead of the value? :) I was thinking that I could just write element.text instead of element.value, but this gives null values. Not trying to be a pain in the *** here, just want to try and understand the framework really :) Thanks a lot in advance!
  • RP Niemeyer
    RP Niemeyer almost 13 years
    Hello- the checkbox (input) element itself doesn't have any associated text. If your text is in a span, then you could move to the next element and read if off of it. Otherwise, if the text is just out there, then you could move to the input's parent and read its innerText. However, the checked binding still runs off of the value of the input when it is checked and unchecked, so you would still want the value to be proper. If you are interested, I just wrote a post on custom bindings here: knockmeout.net/2011/07/another-look-at-custom-bindings-for.h‌​tml
  • Adaptabi
    Adaptabi over 12 years
    Great job! I love your example!
  • ClearCloud8
    ClearCloud8 about 11 years
    Why is it required (when also using the checked binding) to use "attr: { value: $data }" instead of just using "value: $data" ? I was setting up my checkbox list and kept getting weird bugs like I had to click the checkbox twice for it to get checked. This was only resolved when I switched to using the attr binding to wrap the value binding. Any reason why the attr binding is required, when used in conjunction with the checked binding?
  • RP Niemeyer
    RP Niemeyer about 11 years
    @ClearCloud8 - the value binding tries to setup two-way binding, so it attaches events to the input to try to update the model. When using the checked binding you really want to just set the value attribute of the element rather than attach additional handlers that can cause the issues that you mentioned.
  • b.b3rn4rd
    b.b3rn4rd over 9 years
    since version 2.0 use computed instead of dependentObservable
  • Jon S
    Jon S over 6 years
    Aren't radio buttons mutually-exclusive checkboxes?