Mark
Mark

Reputation: 7818

jQuery and KnockOutJS linking a slider to the Cart example

Using the cart example from KnockOutJS.com - how would you link up a slider (or number of sliders), so that the Total Value, takes account of the drop down lists, and the number of additional items (sunglasses in this case) selected in the Slider control.

I have got the Total Value calling the getExtrasTotal() function - when the drop down lists, or number of items is changed - but not when the sliders are changed.

There is a fiddle here for it: http://jsfiddle.net/mtait/mBxky/1/

HTML:

<div class='liveExample'> 

<table width='100%'>
    <thead>
        <tr>
            <th width='25%'>Category</th>
            <th width='25%'>Product</th>
            <th class='price' width='15%'>Price</th>
            <th class='quantity' width='10%'>Quantity</th>
            <th class='price' width='15%'>Subtotal</th>
            <th width='10%'> </th>
        </tr>
    </thead>
    <tbody data-bind='foreach: lines'>
        <tr>
            <td>
                <select data-bind='options: sampleProductCategories, optionsText: "name", optionsCaption: "Select...", value: category'> </select>
            </td>
            <td data-bind="with: category">
                <select data-bind='options: products, optionsText: "name", optionsCaption: "Select...", value: $parent.product'> </select>
            </td>
            <td class='price' data-bind='with: product'>
                <span data-bind='text: formatCurrency(price)'> </span>
            </td>
            <td class='quantity'>
                <input data-bind='visible: product, value: quantity, valueUpdate: "afterkeydown"' />
            </td>
            <td class='price'>
                <span data-bind='visible: product, text: formatCurrency(subtotal())' > </span>
            </td>
            <td>
                <a href='#' data-bind='click: $parent.removeLine'>Remove</a>
            </td>
        </tr>
    </tbody>
</table>
<br />
<label for="slider1">Sunglasses $20 each - how many would you like</label>
<input type="range" class="slide" name="slider1" id="slider1" min="0" max="10" value="0" data-price="20.00" data-id="1">
<br />
<label for="slider2">Doc holder $15 each - how many would you like</label>
<input type="range" class="slide" name="slider2" id="slider2" min="0" max="10" value="0" data-price="15.00" data-id="2">


<p class='grandTotal'>
    Total value: <span data-bind='text: formatCurrency(grandTotal())'> </span>
</p>
<button data-bind='click: addLine'>Add product</button>
<button data-bind='click: save'>Submit order</button>

</div>

Javascript:

function formatCurrency(value) {
return "$" + value.toFixed(2);
}

var CartLine = function() {
var self = this;
self.category = ko.observable();
self.product = ko.observable();
self.quantity = ko.observable(1);
self.subtotal = ko.computed(function() {
    return self.product() ? self.product().price * parseInt("0" + self.quantity(), 10) : 0;
});

// Whenever the category changes, reset the product selection
self.category.subscribe(function() {
    self.product(undefined);
});
};

var Cart = function() {
// Stores an array of lines, and from these, can work out the grandTotal
var self = this;
self.lines = ko.observableArray([new CartLine()]); // Put one line in by default
self.grandTotal = ko.computed(function() {
    var total = 0;
    $.each(self.lines(), function() { total += this.subtotal() })
    var 
    return total;
});

// Operations
self.addLine = function() { self.lines.push(new CartLine()) };
self.removeLine = function(line) { self.lines.remove(line) };
self.save = function() {
    var dataToSave = $.map(self.lines(), function(line) {
        return line.product() ? {
            productName: line.product().name,
            quantity: line.quantity()
        } : undefined
    });
    alert("Could now send this to server: " + JSON.stringify(dataToSave));
};
};

function getExtrasTotal() {
var extrastotal = 0;
var count = 0;
var arr = [];
$('.slide')
     .each(function (index, Element) {
         var obj = {
             id: $(Element).data("id"),
             price: $(Element).data("price"),
             number: $(Element)
                 .val()
         };
         extrastotal += obj.number * obj.price;
     });
return extrastotal;
}

ko.applyBindings(new Cart());

Thank you,

Mark

Upvotes: 0

Views: 269

Answers (1)

dan.p
dan.p

Reputation: 416

First, you need to make sure that the sliders also updates a ko.observable.

Then, the extrasTotal also need to be a ko.computed like the grandTotal, but observing the extras rather then the cart lines.

I updated your fiddle with some quick-and-dirty:

<input type="range" class="slide" name="slider1" id="slider1" min="0" max="10" data-bind="value: sunglasses">

and

self.extrasTotal = ko.computed(function() {
    var sunglasses = self.sunglasses(); 
    var docholders = self.docholders(); 
    return sunglasses * self.sunglassPrice + docholders * self.docholderPrice; 
}); 

Of course, in reality you probably want to make it into an array of possible extras and render the <input>:s with a foreach, and take the price from a database.

This fiddle hints on how to do that:

self.extrasTotal = ko.computed(function() {
    var total = 0; 
    ko.utils.arrayForEach(self.extras(), function (extra) {
        var count = extra.count(); 
        total = total + (count * extra.price); 
    });
    return total; 
}); 

Upvotes: 1

Related Questions