Crumblenautjs Crumblenautjs - 8 days ago 4
TypeScript Question

Knockout Checkboxes using Typescript

Context: I have a table with a list of checks. Each check has a checkbox and the table itself has a "select all" check box at the top. The purpose for the check boxes is to print the checks.

I currently have the select all check box working - it adds all of the checks to a chosenChecks ko.observable array as well as the checkIDs to a CheckIDs array. The server's print function uses a list of checkIDs.

The issue I am having is with the adding and removing of individual checks/checkIDs.. I'm not sure how to determine if the check/checkID is already in the array, and if so, how to remove it. Here's the code, with the views etc.

Disclaimer: I might be over thinking this in how I've setup all of my arrays.

Thanks for all your help.

The View:

<table class="details_table" data-bind="visible: vendorChecks().length">
<thead>
<tr>
<th>Check ID
</th>
<th>Check Date
</th>
<th>Vendor Name
</th>
<th>Check Amount
</th>
<th>Approve Status
</th>
<th>
<input type="checkbox" data-bind="checked: selectAllChecks" title="Select all/none"/>
</th>

</tr>
</thead>
<tbody class="nohighlight" data-bind="foreach: $root.vendorChecks">
<tr>
<td><span data-bind="text: $data.CheckID"></span></td>
<td><span data-bind="text: CheckDate"></span></td>
<td><span data-bind="text: VendorName"></span></td>
<td><span data-bind="text: FormatCurrency(CheckAmount())"></span></td>
<td><span data-bind="text: Globalize.formatCheckRunApproveStatus(ApprovalStatusID())"></span></td>
<td>
<input type="checkbox" class="print_line_checkbox" data-bind="checkedValue: $data, checked: $root.chosenChecks(), click: $root.addCheck"/>
</td>
</tr>
</tbody>
</table>


The Typescript:

class SearchPrintedChecksModel {
public checkRuns = ko.observableArray<CheckRunModel>(null);
public bankDrafts = ko.observableArray<BankDraftInfoModel>(null);
public vendorChecks = ko.observableArray<BankDraftInfo>(null);
public isSelectedCheck = ko.observable(false);
public chosenChecks = ko.observableArray<BankDraftInfo>(null);
public checkIDs = ko.observableArray();

public addCheck(checkIDs) {
var checks = printModel.chosenChecks();
const CheckIDs = checkIDs;
for (var i in checks) {
checkIDs.push(checks[i].CheckID);
???
}
}
public selectAllChecks = ko.pureComputed({
read: function () {
return this.chosenChecks().length === this.vendorChecks().length;
},
write: function(value) {
this.chosenChecks(value ? this.vendorChecks.slice(0) : []);
const checks = printModel.chosenChecks();
const checkIDs = printModel.checkIDs();
for (let i in checks) {
if (checks.hasOwnProperty(i)) {
printModel.checkIDs.push(checks[i].CheckID);
}
}
this.addCheck(checkIDs);
},
owner: this
});

}


$(document).ready(() => {
ko.applyBindings(printModel);
});

Answer

Rather than finding the error in your logic, I'd suggest looking in to a slightly different structure:

  • Put a checked observable in each of the items. Use this observable in the checked data-bind.
  • Create a computed with a read and write method in the parent view model.
    • The read function checks if all items are checked
    • The write function passes the written value to each item

Here's what the code would look like:

function ViewModel() {
  
  this.items = [
    { id: 1, checked: ko.observable(false) },
    { id: 2, checked: ko.observable(false) },
    { id: 3, checked: ko.observable(false) },
    { id: 4, checked: ko.observable(false) },
  ];
  
   this.allChecked = ko.computed({
     read: function() {
       return this.items.every(function(item) {
         return item.checked();
       });
     },
     write: function(value) {
        this.items.forEach(function(item) {
          item.checked(value);
        });
     }
   }, this);
}
    
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<label>
  <input type="checkbox" data-bind="checked: allChecked">
  all
</label>
<ul data-bind="foreach: items">
  <li>
    <label>
      <input type="checkbox" data-bind="checked: checked"/>
      <span data-bind="text: 'Item ' + id"></span>
    </label>
  </li>
</ul>