Using knockout, I have a select (a list of names) whose options are bound to another set of knockout-bound data (people). When the name of any person changes, the value of the select option that is bound to that person's name is correctly updated. However, the select's selection is not preserved if you had that person selected already.
See this jsFiddle for a live example: http://jsfiddle.net/DbBZQ/
- Select "Jane" from the list.
- Change the name "Jane" to something else ("Jane Doe" for example).
- Notice the select defaults back to the first item.
How can I make the selection stick to the same option index even if the underlying value has changed? Is there a way to instruct knockout to preserve the selection or do I have to do this separately using JS?
Complete Code Example
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
var data =
{
people: ko.observableArray(
[
{ name: ko.observable("Jim") },
{ name: ko.observable("Jane") },
{
name: ko.observable("Sam"),
subordinates: ko.observableArray(
[
{
name: ko.observable("Tambone"),
subordinates: ko.observableArray(
[
{ name: ko.observable("Edward") },
{ name: ko.observable("Kristy") },
{ name: ko.observable("Thomas") },
{ name: ko.observable("Andy") }
])
},
{ name: ko.observable("Jules") }
])
}
])
};
var allNames = ko.computed(function ()
{
var names = [];
var selector = function (name, indent)
{
var option =
{
value: name,
text: (indent || "") + name
};
return option;
};
for (var i = 0; i < data.people().length; i++)
{
names.push(selector(data.people()[i].name()));
addSubordinates(names, 1, data.people()[i].subordinates, selector);
}
return names;
});
function addSubordinates(names, depth, subordinates, selector)
{
if (subordinates != null)
{
var indentText = "";
for (var i = 0; i < depth; i++)
indentText += ". . ";
for (var i = 0; i < subordinates().length; i++)
{
names.push(selector(subordinates()[i].name(), indentText));
addSubordinates(names, depth + 1, subordinates()[i].subordinates, selector);
}
}
}
</script>
</head>
<body>
<div data-bind="foreach: data.people">
<input type="text" data-bind="value: name" /><br />
</div>
<a href="JavaScript:data.people.push({ name: ko.observable('New Person') });">Add Person</a>
<br /><br /><br />
<select data-bind="options: allNames, optionsValue: 'value', optionsText: 'text', optionsCaption: 'All Names...'" />
<script type="text/javascript">
ko.applyBindings();
</script>
</body>
</html>