Floating DIVs built from JS Array with Knockout Binding and computed values

by nwinkler

HTML

<script src="https://github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.debug.js"></script>
<script src="https://raw.github.com/SteveSanderson/knockout.mapping/master/knockout.mapping.js"></script>
<div class='container' id='fieldContainer' data-bind='foreach: labels'>
    <div class='field' data-bind='attr: { id: "field_" + $parent.requestId() +  "_" + id() }'>
        <span class='label' data-bind='text: label'></span>: <span class='value' data-bind='text: value'></span>
    </div>
</div>
<button id='update'>Update</button>
<button id='delete'>Delete</button>
<button id='reset'>Reset</button>

CSS

.container {
    overflow: hidden;
    width: 100%;
    margin: -5px;
}

.field {
    background-color: gray;
    height: 20px;
    width: 100px;
    margin: 5px;
    float: left;
    text-align: left;
    line-height: 20px;
}

.label {
    margin: 2px;
    float: left;
}

.value {
    margin: 2px;
    float: right;
}

JavaScript

var fields = [ { id: 1, text: 'Darkness' },
    { id: 5, text: 'Of' },
    { id: 2, text: 'On' },
    { id: 6, text: 'Town' },
    { id: 3, text: 'The' },
    { id: 4, text: 'Edge' }
];

var fields2 = [ { id: 1, text: 'We\'re' },
    { id: 2, text: 'Going' },
    { id: 3, text: 'Racing' },
    { id: 4, text: 'In' },
    { id: 5, text: 'The' },
    { id: 6, text: 'Streets' }
];

var labels = [ { id: 1, label: '1' },
    { id: 2, label: '2' },
    { id: 3, label: '3' },
    { id: 4, label: '4' },
    { id: 5, label: '5' },
    { id: 6, label: '6' }
];

var fieldMapping = {
    key: function(item) {
        return ko.utils.unwrapObservable(item.id);
    }
};

var labelMapping = {
    create: function(options) {
        return (new (function() {
            this.value = ko.computed(function() {
                var index = viewModel.fields.mappedIndexOf( { id: options.data.id } );
                
                if (index < 0) {
                    return "n/a";
                }
                else {
                    return viewModel.fields()[index].text();
                }
            }, this);
            
            this.value.subscribe(function(data) {
                $("#field_" + viewModel.requestId() + "_" + this.id()).effect('highlight');
            }, this);

            ko.mapping.fromJS(options.data, {}, this);
        }) ());
    }
};

var viewModel = {
    labels: ko.mapping.fromJS([], labelMapping),
    
    fields: ko.mapping.fromJS([], fieldMapping),
    
    requestId: ko.observable("3")
};

ko.applyBindings(viewModel);

function updateFields(updatedFields) {
    $.each(updatedFields, function(index, value) {
        var indexInViewModel = viewModel.fields.mappedIndexOf( { id: value.id } );
        
        if (indexInViewModel < 0) {
            viewModel.fields.push(ko.mapping.fromJS(value));
        }
        else {
            ko.mapping.fromJS(value, fieldMapping, viewModel.fields()[indexInViewModel]);
        }
    });
}

function...