2
votes

I've got a question regarding checkedValue of checked binding in Knockout (http://knockoutjs.com/documentation/checked-binding.html)

Basically, look this fiddle http://jsfiddle.net/jo9dfykt/1/. Why if I click the button "Add Item" the right choise is selected but if I click the second button "Add Item Two" this not succed? What are the differences between addItem and addItem2 method in viewModel?

And then, why the with selectedValue shows nothing?

My goal is to bind a list of radio button to an observableArray and save the entire object into an observable. But I want to set the inital value of radio button without search it on observableArray.

Javascript

var viewModel = {
    items: ko.observableArray([
        { itemName: 'Choice 1' },
        { itemName: 'Choice 2' }
    ]),
    chosenItem: ko.observable(),
    addItem: function() {
                this.chosenItem(this.items()[0]);
    },
    addItem2: function() {
                this.chosenItem({itemName: 'Choice 2'});
    }
};
viewModel.chosenItem.subscribe(function(newValue){
        console.log(newValue.itemName);
    }),   
ko.applyBindings(viewModel);

HTML

<!-- ko foreach: items -->
<input type="radio" data-bind="checkedValue: $data, checked: $root.chosenItem" />
<span data-bind="text: itemName"></span>
<!-- /ko -->
<input type="button" data-bind="click: addItem" value="Add Item"/>
<input type="button" data-bind="click: addItem2" value="Add Item Two"/>
<div>Selected Value: <span data-bind="text: chosenItem.itemName"></span></div>
2

2 Answers

2
votes

Knockout computes the checked state by doing a reference check. I.e.:

checkedValue = $data;                   // from checkedValue: $data
checked = checkedValue === chosenItem() // from checked: $root.chosenItem

You'll probably know that, in javascript, two objects that look similar are not equal:

{ a: 1 } === { a: 1 } // returns false

That's why you need to rewrite your second button to:

this.chosenItem({itemName: this.items()[0]});

to make sure the reference check passes.

Also, to correctly show the current state, use:

<div data-bind="with: chosenItem">
  Selected Value: <span data-bind="text: itemName"></span>
</div>

If you want to circumvent having to point to the elements in your items array, you can use checkedValue: itemName and addItem() { this.chosenItem("Choice 1"); } but this forces you to use unique names.

A better option would be to make viewmodels for your items that have their own add method:

var Item = function(name, selection) {
  var self = this;
  this.itemName = name;
  this.add = function() {
    selection(self);
  };
};

var items = ["Choice 1", "Choice 2"].map(function(name) {
  return new Item(name, vm.chosenItem);
});
1
votes

In the first example you're providing a reference to the actual item you want selected. In the second you're providing an object which has the same property & value, but its not the actual item in the list merely one which looks like it.

But I want to set the inital value of radio button without search it on observableArray.

Perhaps make chosentItem a computed observable, which relies on a string observable with just the key of the item you want selected. This would allow you to say something like:

self.chosenItemName('Choice2');