Relational data (with index)

Adding Ryan N's suggested index function

by spring1975

HTML

<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.debug.js"></script>
<script src="https://raw.github.com/SteveSanderson/knockout.mapping/master/knockout.mapping.js"></script>
<script src="https://raw.github.com/timrwood/moment/master/moment.js"></script>
<div id="ViewModel">
        <button class="getData">Get Data</button>
        <button data-bind="click: updateAttachmentsProgress">Update Attachments Progress</button>

        <ul class="worklist" data-bind="foreach: worklistRequests">
            <li>
                <div class="progress" data-bind="progressBar: IDRequest, root: $root"></div>
                <div><span data-bind="text: IDRequest"></span> &ndash; <strong data-bind="text: name"></strong> <!--<span data-bind="text: '(' + attachmentsProgress.percentDone() + '%)'"></span>--></div>
            </li>
        </ul>

    </div>
    <pre id="ViewModelUnmapped" data-bind="text: unMappedVM()"></pre>

CSS

{
	margin-left: 450px;
	color: #00ff00;
	width: 550px;
	background-color: black;
	overflow: auto;
	height: 900px;
}
#ViewModel {
	float: left;
}
.progress
{
	background-color:#CC0000;
	height: 2px;
}
.worklist li {
	border: 1px solid #CCC;
}

JavaScript

var viewModel = {}, worklistRequestModel = {}, attachmentsProgressModel = {};

  
WorklistRequestModel = function(data) {
    worklistRequestModel = ko.mapping.fromJS(data, {}, this);
},
AttachmentsProgressModel = function(data) {
    ko.mapping.fromJS(data, {}, this);
},
RootModel = function(data, mappingOptions) {
    var rootModel = ko.mapping.fromJS(data, mappingOptions);
    return rootModel;
},
mappingOptions = {
    'attachmentsProgress': {
        key: function (data) {
            return ko.utils.unwrapObservable(data.IDRequest);
        },
        create: function (options) {
            return new AttachmentsProgressModel(options.data);
        }
    },
    'worklistRequests': {
        key: function (data) {
            return ko.utils.unwrapObservable(data.IDRequest);
        },
        create: function (options) {
            return new WorklistRequestModel(options.data);
        }
    }
},
rootMapping = {
    create: function (options) {
        return new RootModel(options.data, mappingOptions);
    }
},
    
loadData = function (newData) {
    if ($.isEmptyObject(viewModel)) {
        viewModel = ko.mapping.fromJS(newData, rootMapping);
        ko.applyBindings(viewModel);
    } else {
        ko.mapping.fromJS(newData, viewModel);
    }
},
updateAttachmentsProgress = function () {
    ko.mapping.fromJS({ "attachmentsProgress": myFauxAttachmentsProgress2 }, mappingOptions, viewModel);
},
unMappedVM = function () {
    return ko.toJSON(ko.mapping.toJS(viewModel), null, 2);
};

ko.bindingHandlers.progressBar =
{
    init: function (element, valueAccessor, allBindingsAccessor) {
        var percentDone = ko.computed(function () {
            var allBindings = allBindingsAccessor();

            var progressItem = allBindings.root.attachmentsProgress.findByPropVal('IDRequest', valueAccessor());

            return progressItem ? progressItem.PercentDone() : 0;
        });
        $(element).css('width', percentDone() + '%');
        if (percentDone()...