Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working with a list of checkboxes in knockoutjs

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!

like image 330
bomortensen Avatar asked Jul 18 '11 16:07

bomortensen


2 Answers

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>
like image 176
RP Niemeyer Avatar answered Oct 21 '22 13:10

RP Niemeyer


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" />
like image 45
jwize Avatar answered Oct 21 '22 12:10

jwize