KnockoutJS & jQuery Datatable custom binding (jQueryUI)

a custom knockout binding works on vanilla 3.4 knockout

by Shrikrishna Gupta

HTML

<script src="//cdn.datatables.net/1.10.7/js/jquery.dataTables.min.js"></script>
<script src="//code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.3/themes/smoothness/jquery-ui.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.3.5/knockout.mapping.js"></script>
<button data-bind="click: add">ADD</button>&nbsp;&nbsp;Toggle Editing Mode <span data-bind="text: getEditMode"></span>

<input type="checkbox" data-bind="checked: inline" />
<br />
<br/>
<button data-bind="click: removeAll">Remove All</button>
<br />
<br />
<div id="wrapper">
  <table id="example" style="width:100%" class="table table-striped table-bordered dataTable">
    <thead>
      <tr>
        <th>ID</th>
        <th>Name</th>
        <th>Age</th>
        <th>Remove</th>
        <th>Edit</th>
      </tr>
    </thead>
    <tbody data-bind="dataTablesForEach: {data: people, dataTableOptions: {
                          jQueryUI: true,
                          scrollY: 250,
                          paging: true,
                          dom: 'rtip',
                          columns:[
                              {width: '30px'},
                              {width: '100px'},
                              {width: '100px'},
                              {width: '100px'},
                              {width:'100px'}
                          ]
                      }
                  }">
      <tr style="height:50px;">
        <td><span data-bind="text: id" />
        </td>
        <td style="width:50px;"> <span data-bind="text: first, visible:!isEdit()"></span>

          <input class="form-control input-sm" data-bind="textInput: first, visible: isEdit()" style="width:95%" />
        </td>
        <td> <span data-bind="text: age, visible:!isEdit()"></span>

          <input class="form-control input-sm"...

CSS

.ui-buttonset .ui-button {
  margin-left: 0;
  margin-right: 0;
}

body {
  background: white;
}

JavaScript

ko.bindingHandlers.dataTablesForEach = {
  page: 0,
  init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    var options = ko.unwrap(valueAccessor());
    ko.unwrap(options.data);
    if (options.dataTableOptions.paging) {
      valueAccessor().data.subscribe(function(changes) {
        var table = $(element).closest('table').DataTable();
        ko.bindingHandlers.dataTablesForEach.page = table.page();
        table.destroy();
      }, null, 'arrayChange');
    }
    var nodes = Array.prototype.slice.call(element.childNodes, 0);
    ko.utils.arrayForEach(nodes, function(node) {
      if (node && node.nodeType !== 1) {
        node.parentNode.removeChild(node);
      }
    });
    return ko.bindingHandlers.foreach.init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
  },
  update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var options = ko.unwrap(valueAccessor()),
      key = 'DataTablesForEach_Initialized';
    ko.unwrap(options.data);
    var table;
    if (!options.dataTableOptions.paging) {
      table = $(element).closest('table').DataTable();
      table.destroy();
    }
    ko.bindingHandlers.foreach.update(element, valueAccessor, allBindings, viewModel, bindingContext);
    table = $(element).closest('table').DataTable(options.dataTableOptions);
    if (options.dataTableOptions.paging) {
      if (table.page.info().pages - ko.bindingHandlers.dataTablesForEach.page == 0)
        table.page(--ko.bindingHandlers.dataTablesForEach.page).draw(false);
      else
        table.page(ko.bindingHandlers.dataTablesForEach.page).draw(false);
    }
    if (!ko.utils.domData.get(element, key) && (options.data || options.length))
      ko.utils.domData.set(element, key, true);
    return {
      controlsDescendantBindings: true
    };
  }
};

// Person ViewModel
var Person = function(data, parent) {
  var that = this;
  this.id = data.id;
  this.isEdit =...