table filter knockout binding

by wwwroot

HTML

<script src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<table class="table">
    <thead>
        <tr>
            <th>name</th>
            <th>category</th>
            <th>price</th>
        </tr>
        <tr>
            <th>
                <select data-bind="columnFilter: { data: items, filter: 'name', caption: 'all' }, value: nameFilter" />
            </th>
            <th>
                <select data-bind="columnFilter: { data: items, filter: 'category', caption: '--' }" />
            </th>
            <th>
                <select data-bind="columnFilter: { data: items, filter: 'price', caption: '--' }" />
            </th>
        </tr>
    </thead>
    <tbody data-bind="foreach: items">
        <tr>
            <td data-bind="text: name"></td>
            <td data-bind="text: category"></td>
            <td data-bind="text: price"></td>
        </tr>
    </tbody>
</table>

CSS

table {
    max-width: 100%;
    background-color: transparent;
    border-collapse: collapse;
    border-spacing: 0;
}
.table {
    width: 100%;
    margin-bottom: 20px;
}
.table th, .table td {
    padding: 8px;
    line-height: 20px;
    text-align: left;
    vertical-align: top;
    border-top: 1px solid #dddddd;
}
.table th {
    font-weight: bold;
}
.table thead th {
    vertical-align: bottom;
}
.table caption + thead tr:first-child th, .table caption + thead tr:first-child td, .table colgroup + thead tr:first-child th, .table colgroup + thead tr:first-child td, .table thead:first-child tr:first-child th, .table thead:first-child tr:first-child td {
    border-top: 0;
}
.table tbody + tbody {
    border-top: 2px solid #dddddd;
}
.table .table {
    background-color: #ffffff;
}
.table-condensed th, .table-condensed td {
    padding: 4px 5px;
}

JavaScript

ko.bindingHandlers.columnFilter = {
    init: function (element, valueAccessor, allBindings) {
        var options = valueAccessor();
        var data = ko.utils.unwrapObservable(options.data);

        var caption = options.caption || '';
        $(element).append($('<option></option>').val("_0_").html(caption));
        var items = options.filter ? _.map(data, options.filter) : data;

        _.forEach(_.uniq(items).sort(), function (item) {
            $(element).append($('<option></option>').val(item).html(item));
        });
    }
};

var JSONdataFromServer = [{
    "name": "Peach",
        "category": "Fruits",
        "price": 1
}, {
    "name": "Plum",
        "category": "Fruits",
        "price": 0.75
}, {
    "name": "Donut",
        "category": "Bread",
        "price": 1.5
}, {
    "name": "Milk",
        "category": "Dairy",
        "price": 4.50
}, {
    "name": "Milk",
        "category": "Dairy",
        "price": 1.99
}, {
    "name": "Beer",
        "category": "Alco",
        "price": 4.50
}];

var viewModel = {
    categories: ["Bread", "Dairy", "Fruits", "Vegetables"],
    source: ko.observableArray(JSONdataFromServer),
    items: ko.observableArray(JSONdataFromServer),
    nameFilter: ko.observable()
};
viewModel.nameFilter.subscribe(function (value) {
    if (value != "_0_") {
        var arr = _.filter(viewModel.items(), {
            'name': value
        });
        console.log(arr);
    }
});
ko.applyBindings(viewModel);