The rest of the owl: Knockout BindingHandlers with Typescript?
Asked Answered
C

1

6

I am trying in earnest to follow the accepted answer for this. I'm using Knockout, Typescript, MVC, VS2017 and node modules for most of my packages. The problem I'm having is that the binding does not get called but don't have any build errors.

provide a definition file (say myBindings.d.ts)

add the following code

  interface KnockoutBindingHandlers {
        csharpTypes: KnockoutBindingHandler;
    }

Reference this definition file in your extensions.ts file


Thus I created a customKoBindings.d.ts like this.

/// <reference path="../../node_modules/@types/knockout/index.d.ts" />

interface KnockoutBindingHandlers {
    dataTablesForEach: KnockoutBindingHandler;
}

I try to apply the binding in my cshtml. Do I need to pull in the ko.bindingHandler into my ViewModel in some way for it to be available for the view?

<table style="width: 100%" id="copyEmpTrainingSearchResultsTable" class="display" cellspacing="0">
                                                <thead>
                                                    <tr>
                                                        <th data-bind="sort: { arr: employees, prop: 'firstName' }">First Name</th>
                                                        <th data-bind="sort: { arr: employees, prop: 'lastName' }">Last Name</th>
                                                        <th data-bind="sort: { arr: employees, prop: 'jobTitle' }">Job Title</th>
                                                        <th data-bind="sort: { arr: employees, prop: 'primaryManager' }">Manager</th>
                                                        <th data-bind="sort: { arr: employees, prop: 'department' }">Department</th>
                                                        <th>Trainings</th>
                                                    </tr>
                                                </thead>
                                                <tbody data-bind="dataTablesForEach: {data: trainingEmployeeSearchResults, dataTableOptions: {}}">
                                                    <tr>
                                                        <td data-bind="text: firstName"></td>
                                                        <td data-bind="text: lastName"></td>
                                                        <td data-bind="text: jobTitle"></td>
                                                        <td data-bind="text: primaryManager"></td>
                                                        <td data-bind="text: department"></td>
                                                        <td><button data-bind="click:$parent.seeTrainings"><i class="fa fa-eye"></i></button></td>
                                                    </tr>
                                                </tbody>
                                            </table>

The answer says to add the binding to extensions.ts, so I created a new file. Binding code comes from here. How would I reference the interface I created? What should be pointing to this file?

extensions.ts

import * as ko from "knockout"
import * as $ from "jquery";

export class KnockoutExtensions {
    // Constructor
    constructor() {
        ko.bindingHandlers.dataTablesForEach = {
            page: 0,
            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                console.log("init for dataTablesForEach");
                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: 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) {
                console.log("update for dataTablesForEach");
                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 };
            }
        }; 
    }
}

Update:

In the DefinitelyTyped documentation's test they just do this, which I've now tried in the constructor of my viewModel but init and update still are not called:

 (<any>ko.bindingHandlers).countInits = { init: function() { initCalls++ } };

Update 2:

I have not used requireJS in this project. Is it needed for linking the KnockoutBindingHandler in?


Update 3:

After reviewing this article, I've attempted to reference the above customKoBindings.d.ts with the node modules reference in my tsconfig.json. This did not it fix either.

{
  "compilerOptions": {
    "outDir": "./wwwroot/build/",
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "es6",
    "module": "amd",
    "moduleResolution": "node"
  },
  "files": [
    "./Scripts/Common/customKoBindings.d.ts"
  ],
  "exclude": [
    "node_modules",
    "wwwroot"
  ]
}

Update Lost Count: It works!

added this to the top of extensions.ts:

/// <reference path="./customKoBindings.d.ts" />

in my main.ts added this:

import { KnockoutExtensions} from "./Common/extensions"
const koExt = new KnockoutExtensions();
Coheir answered 18/12, 2017 at 21:52 Comment(1)
work around for the ultimate goal of keeping ko and datatable in sync #8823090Coheir
C
4

added this to the top of extensions.ts:

/// <reference path="./customKoBindings.d.ts" />

in my main.ts added this:

import { KnockoutExtensions} from "./Common/extensions"
const koExt = new KnockoutExtensions();
Coheir answered 19/12, 2017 at 22:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.