Knockout issue with optionsValue and observable array


For this piece of code

function Order(name, initialMeal) {
    var self = this;
    self.name = name;
    self.meal = ko.observable(initialMeal);
}

function OrdersViewModel() {
    var self = this;
    self.availableMeals = [
        { menuItem:"Chicken", calories:1000, price:12 },
        { menuItem:"Eggs", calories:900, price:10 },
        { menuItem:"Fries",calories:700, price:15 }
    ];

    self.orders = ko.observableArray([
        new Order("Mike", self.availableMeals[0]),
        new Order("John", self.availableMeals[0]),
        new Order("Larry", self.availableMeals[0])
    ]);
    self.addOrder = function(){
        self.orders.push(new Order("", self.availableMeals[0]));
    }
}
ko.applyBindings(new OrdersViewModel());

I have the following view

 <tbody data-bind="foreach: orders">
        <tr>
            <td><input data-bind="value: name" /></td>
            <td><select data-bind="options: $root.availableMeals, value: meal, optionsValue: 'price', optionsText: 'menuItem'"></select></td>
            <td data-bind="text: meal().price"></td>
        </tr>
    </tbody>

You can probably find it in the knockout examples. The problem is the last binding meal().price doesn't seem to work when optionsValue exists above it. If I remove optionsValue it works just fine. Am I missing something? Thanks.

Yes, you are missing something.

Without specifying optionsValue, you are binding a meal object (from the availableMeals array) to the meal property of the order. The price binding looks for the price property on of the meal that you bound, and it finds it, because it finds this object:

{ menuItem:"Eggs", calories:900, price:10 }

This works.

When you specify the optionsValue as price, you are binding a number to the meal property of the order. The price binding looks for the price property on that number, which does not exist.

This is all correct behavior. You should only use optionsValue when you actually need the property you are specifying. This is usually only necessary when you want to bind onto the ID of some list you have bound to a dropdown, like one you might get from a database. Since you are using other properties on the objects that you are binding to (price), you are best served by letting it bind the entire object so that you can use the entire object later.