user1104854
user1104854

Reputation: 2167

Setting observable from select list in knockout.js

I'm trying to use the value of a select list for a calculation but I can't get it to retrieve the number when it's selected.

When I return the selected quantity it returns some javascript instead of the number

selectedQuantity is the value I'm trying to retrieve.

<table border="1">
    <thead><tr>
        <th>ID</th><th>Inventory</th><th>Quantity</th><th>Price</th><th>Total</th><th></th>
    </tr></thead>
    <tbody data-bind="foreach: itemNumbers">
        <tr>
            <td data-bind="text: ID"></td>
            <td data-bind="text: inventory"></td>
            <td><select data-bind="quantityDropdown: inventory, value: selectedQuantity"></select></td>
            <td data-bind="text: formattedPrice"></td>
            <td data-bind="text: itemTotal"></td>
            <td><a href="#" data-bind="click: $root.removeItem">Remove</a></td>
        </tr>    
    </tbody>
</table>

Here's the javascript

function ItemEntry(ID, inventory, price) {
    var self = this;
    self.ID = ID;
    self.inventory = inventory;
    self.price = price;

    self.selectedQuantity = ko.observable(); //returned value (trying to get it at least!)

    self.itemTotal = ko.computed(function() {
        var price = self.price;
        var quantity = self.selectedQuantity;
        return quantity; //just returning quantity for now to verify correct value is selected
    });


    self.formattedPrice = ko.computed(function() {
        var price = self.price;
        return price ? "$" + price.toFixed(2) : "None";        
    });
}

function EntryViewModel(newItem) {
    var self = this;
    self.newItem = newItem;


    //start the array with some items
    self.itemNumbers = ko.observableArray([
        new ItemEntry("1", 20, 22.50) //ID, inventory, price 
    ]);

    // Computed data
   self.totalCost = ko.computed(function() {
       var total = 0;
        for (var i = 0; i < self.itemNumbers().length; i++) {
            total += Number(self.itemNumbers()[i].price);
        }
       return total;
    });    

    self.removeItem = function(item) { self.itemNumbers.remove(item) }
}

//populate the select list with values up to the number in inventory (ex, if inventory is 3, it will fill with 0-7)
ko.bindingHandlers.quantityDropdown = {
    update: function(quantityDropdown, inventory, EntryViewModel) {
        var quantity = ko.utils.unwrapObservable(inventory());
        for(var i = 0; i <= inventory(); i++){
            $(quantityDropdown).append('<option value="' + i + '">' + i + '</option>');
        }
    }
};



ko.applyBindings(new EntryViewModel());

Upvotes: 0

Views: 346

Answers (1)

Joseph Gabriel
Joseph Gabriel

Reputation: 8510

Looks like self.selectedQuantity is defined as (set to) a ko.observable. Observables are functions, so in order to retrieve the value, you should call it like a function:

self.itemTotal = ko.computed(function() {
    var price = self.price;
    //notice the parentheses here to execute the selectedQuantity observable and extract the variable.
    var quantity = self.selectedQuantity();  
    return quantity; //just returning quantity for now to verify correct value is selected
});

Also, it's good to understand how ko.computed functions work. A ko.computed value will automatically update to reflect changes to any ko.observables that are referenced therein. However, the dependency tracking mechanism uses the retrieval of observable values to detect that an observable is being used and should be tracked.

In other words, if you want a ko.computed to update whenever a value changes, you need to reference that value by executing the observable variable as in the code sample above.

The KnockOut documentation says it a lot better than I can: http://knockoutjs.com/documentation/observables.html http://knockoutjs.com/documentation/computedObservables.html.

Upvotes: 2

Related Questions