Knockout - MVVM with Revealing Module Pattern

by johnpapa

HTML

<script src="https://github.com/downloads/SteveSanderson/knockout/knockout-2.0.0.debug.js"></script>
    <div class="page">
        <div class="header" data-bind="with: metadata">
            <h1 data-bind="text: pageTitle"></h1>
            <div data-bind="with:personal">
                <a href="00-index.html">Index</a>
                <a data-bind="attr: {href: link, title: text}, text: text" class="personalCss"></a>
            </div>
        </div>
        <div id="main">
            <div class="showroom">
                <div>
                    <span data-bind="text: products().length"></span>
                    <span>Products</span>
                    <br/>
                    <button data-bind="click: load">Load (via ko binding)</button>
                </div>
                <div class="resultsPanel">
                    <ul data-bind="foreach:products">
                        <li class="guitarListCompact">
                            <div class="photoContainer">
                                <img data-bind="visible: photoUrl, attr: { src: photoUrl }" class="photoThumbnail"></img>
                            </div>
                            <div data-bind="text: $parent.formatCurrency(salePrice)">
                            </div>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    </div>

CSS

body {padding: 10px; font: 12px 'SansationRegular', Arial, sans-serif;}
.page {    
}
.showroom {
}
.header 
{
    margin-bottom: 20px;
}
span {
    font: 12px 'SansationRegular', Arial, sans-serif;
    margin: 0 4px 0 4px;
}
/*.text*/
.caption {
    font: 12px 'SansationBold', Arial, sans-serif;
    color: olivedrab;
    
}
.notes {
    font: 12px 'SansationLight', Arial, sans-serif;
    color: gray;
    font-style: italic
}
input, select {
    font: 12px 'SansationRegular', Arial, sans-serif;
    margin: 0 4px 0 4px;
    clear: left;
    color: rgb(47, 123, 163);
}
input[type=text] {
    width: 100px;
}
select {
    width: 120px;
}
.negative {
    color: red;
}
.positive {
    color: green;
}
table {
    margin: 8px;
}
thead {
    font: 14px 'SansationBold', Arial, sans-serif;
}
td {
    border-bottom: 1px solid lightgray;
}
.resultsPanel 
{
    margin: 8px;
    padding: 4px;
    height: 300px;
    width: auto;
    border: gray dashed thin;
    overflow:scroll;
    overflow-x: hidden;
}
.guitarListCompact {padding: 10px; margin: 1px; float: left;vertical-align: middle;margin-left: auto;margin-right: auto;
    list-style: none;
    -o-text-overflow: ellipsis;
    -ms-text-overflow: ellipsis;
    -moz-text-overflow: ellipsis;
    -webkit-text-overflow: ellipsis;
    text-overflow: ellipsis;   
    overflow:hidden;
    width: 125px;
    width: 100px;
    /*border: gray dashed 1px;*/
}
button {
    -moz-box-shadow: inset 0 1px 0 0 #ffffff;
    -webkit-box-shadow: inset 0 1px 0 0 #ffffff;
    -o-box-shadow: inset 0 1px 0 0 #ffffff;
    box-shadow: inset 0 1px 0 0 #ffffff;
    background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #ededed), color-stop(1, #dfdfdf) );
    background:-moz-linear-gradient( center top, #ededed 5%, #dfdfdf 100% );
    
    filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#dfdfdf');
    background-color:#ededed;
    -moz-border-radius:6px;
    -webkit-border-radius:6px;
   ...

JavaScript

var my = {};

var photoPath = "http://johnpapa.net/files/downloads/sample%20images/guitar/";

// The Product function creates new Product Models
function Product() {
    var self = this;
    self.id = ko.observable();
    self.salePrice = ko.observable();
    self.listPrice = ko.observable();
    self.rating = ko.observable();
    self.photo = ko.observable();
    self.itemNumber = ko.observable();
    self.description = ko.observable();
    self.photoUrl = ko.computed(function() {
        return photoPath + self.photo();
    }, self);
};

// The ViewModel
my.showroomViewModel = (function(my) {
    var
        metadata = {
            pageTitle: "Knockout: MVVM using the Revealing Module Pattern",
            personal: {
                link: "http://twitter.com/john_papa",
                text: "@john_papa"
            }
        },
        products = ko.observableArray(),
        formatCurrency = function(value) {
            return "$" + value().toFixed(2);
        },
        load = function() {
            products([]); // reset
            var data = my.sampleData.data;
            $.each(data.Products, function(i, p) {
                my.showroomViewModel.products.push(new Product().id(p.Id).salePrice(p.SalePrice).listPrice(p.ListPrice).rating(p.Rating).photo(p.Photo).itemNumber(p.ItemNumber).description(p.Description));
            });
        };
    return {
        metadata: metadata,
        products: products,
        formatCurrency: formatCurrency,
        load: load
    };
})(my);

// Bind the ViewModel to the View using Knockout
ko.applyBindings(my.showroomViewModel);


// The Model
my.sampleData = (function(my) {
    "use strict";
    var data = {
        Products: [
            {
            "ModelId": 1,
            "SalePrice": 1649.01,
            "ListPrice": 2199.00,
            "Rating": 5,
            "Photo": "Taylor 314-CE Left-Handed Grand Auditorium Acoustic-Electric Guitar.png",
            "CategoryId": 1,
            "ItemNumber":...