Knockout Binding Handler with row callback

Knockout Binding Handler with row callback

by Shrikrishna Gupta

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.15/js/jquery.dataTables.min.js"></script>
<!-- include knockout.js and the dataTable binding handler for this to work -->

<table data-bind="dataTable: { data: people, columns: [ { data: 'name' }, { data: 'age' } ], rowTemplate: 'row-template' }">
  <thead>
    <tr>
      <th>Name</th>
      <th>Age</th>
    </tr>
  </thead>
</table>
<script id="row-template" type="text/html">
  <td><span data-bind="text: name"></span></td>
  <td><span data-bind="text: age"></span></td>
</script>

JavaScript

/*global jQuery: false, ko: false */

(function($, ko) {
    'use strict';

    // based on the work by http://chadmullins.com/
    // some caveats to be aware of:
    // - x-editables: when clicking a sortable column the editable-unsaved css
    //   class gets removed since each row is re-rendered
    // - data-bind on <tr/> is not supported (yet)
    // - avoid data-bind on thead, tbody, etc, it won't work
    ko.bindingHandlers.dataTable = {
      // used for keeping track of table initialization
      initializedKey: '__ko_bindingHandlers_dataTable_initialized',
      // used for caching a row
      rowIndexKey: '__ko_bindingHandler_dataTable_rowIndexKey',
      // init method...
      init: function(element, valueAccessor) {
        var initializedKey = ko.bindingHandlers.dataTable.initializedKey,
          rowIndexKey = ko.bindingHandlers.dataTable.rowIndexKey,
          result = {
            controlsDescendantBindings: true
          },
          rawData = [],
          oldRowCallback,
          column,
          options,
          dataSource,
          dataTable,
          il,
          i;

        // already initialized
        if ($.data(element, initializedKey) === true) {
          return result;
        }

        // get the data table configuration
        options = ko.utils.unwrapObservable(valueAccessor());

        if (!options.columns) {
          throw 'Must provide the columns option.';
        }

        // convert the source data observable into a simple array
        // and handle changes to the source data
        if (options.data && ko.isObservable(options.data) && $.type(options.data()) === 'array') {
          dataSource = options.data;
          options.data = dataSource();

          // cannot use subscribeArrayChanged since the source observable
          // might be a computed observable

          dataSource.subscribe(function(prevValue) {
            // store the previous value for later comparison
            rawData =...