具有FixedColumns扩展名的DataTables的Knockout绑定无法按预期工作

时间:2015-12-11 18:19:51

标签: knockout.js datatables

我正在使用针对DataTables的敲除绑定suggested here。但是,当使用FixedColumns扩展(将原始数据表克隆到新数据表)时,我丢失新数据表与现有 viewmodel / bindingContext 之间的绑定。

例如,在固定列上选择复选框以从表中选择项目将不会按预期运行。

绑定看起来像这样:

ko.bindingHandlers.dataTablesForEach = { 
page: 0,
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
    var binding = ko.utils.unwrapObservable(valueAccessor());

    if (binding.options.paging) {
        binding.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 binding = ko.utils.unwrapObservable(valueAccessor()),
        key = 'DataTablesForEach_Initialized';

    var table;
    if (!binding.options.paging) {
        table = $(element).closest('table').DataTable();
        table.destroy();
    }

    ko.bindingHandlers.foreach.update(element, valueAccessor, allBindings, viewModel, bindingContext);

    table = $(element).closest('table').DataTable(binding.options);

    if (binding.options.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) && (binding.data || binding.length)) {
        ko.utils.domData.set(element, key, true);
    }

    return {
        controlsDescendantBindings: true
    };
}

(See the full working example)

2 个答案:

答案 0 :(得分:1)

哇...这是一个棘手的问题。我想你应该在应用绑定之前使html可用,或者你需要克隆你的数据并应用绑定。第一个选项是不可能的,因为在初始化数据表时会调用fixedColumns插件。这种情况发生在这个绑定中,并且在调用ko.applyBindings时会发生这种情况。矛盾的.. :))

我还想补充一点,因为固定列,你不会丢失绑定。绑定仍然存在。这只是插件创建的新html浮动在原始绑定的html之上。

然而,我做了这个黑客工作......

在调用applybindings后添加此内容...

编辑:需要将事件绑定到文档,因为固定列插件会根据页面动态重写元素。

$(document).on('click','.DTFC_LeftBodyWrapper input[type="checkbox"]',function(){
   var value = $(this).attr('value');
     $('.dataTables_scrollBody').find('input[value="'+value+'"]').click(); });

我想如果你不能绑定克隆的html,那么你可以强制它与原始的html进行交互......它真的很hacky但它​​的工作原理..¯_(ツ)_ /¯

forked working example CODEPEN

答案 1 :(得分:0)

我最终得到了一个绑定,它在创建后将克隆表重新应用于克隆表(即固定列表):

import $ from 'jquery';
import ko from 'knockout';
import 'datatables.net';
import 'datatables.net-fixedColumns';

const defaultOptions = {
    deferRender: true,
    paging: true
};

ko.bindingHandlers.datatables = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        const $element = $(element),
            binding = ko.unwrap(valueAccessor()),
            options = binding.options || {};

        $.extend(true, options, defaultOptions);

        if (binding.rowTemplateId && binding.data) {

            // bind the header first
            ko.applyBindingsToDescendants(viewModel, $element.find('thead')[0]);

            setupTableBody($element, binding, bindingContext);

            if (ko.isObservable(binding.data)) {

                // destroy and build the table again when the data changes
                binding.data.subscribe(() => {
                    $element.DataTable().destroy();
                    $element.find('tbody').remove();

                    setupTableBody($element, binding, bindingContext);
                    initializeDataTable(element, options);
                }, null, 'arrayChange');
            }
        }

        initializeDataTable(element, options);

        ko.utils.domNodeDisposal.addDisposeCallback(element, () => {
            $(element).DataTable().destroy();
        });

        return {
            controlsDescendantBindings: true
        };
    }
};

function initializeDataTable(element, options) {
    const table = $(element).DataTable(options);

    if (options.fixedColumns) {

        // we need to apply the context to the cloned table for the first time
        setTimeout(() => {
            applyBindingsToClonedRows(element, options.fixedColumns);
        }, 0);

        // register handler to fix the cloned table column width
        // when the table is (re)drawn
        table.on('draw.dt', (event) => {
            $(event.target).DataTable().fixedColumns().relayout();
        });

        // register handler to fix the cloned table binding context
        // when the table is (re)drawn
        table.on('draw.dt.DTFC', (event) => {
            applyBindingsToClonedRows(event.target, options.fixedColumns);
        });
    }
}

function setupTableBody($element, binding, bindingContext) {

    // render each element of the body with the template
    let tbody = $element.find('tbody')[0];
    if (!tbody) {
        tbody = document.createElement('tbody');
        $element.append(tbody);
    }
    ko.renderTemplateForEach(ko.unwrap(binding.rowTemplateId), binding.data, {}, tbody, bindingContext);
}

function applyBindingsToClonedRows(originalTable, fixedColumnsOptions) {
    const $table = $(originalTable);
    const rows = $table.find('tbody>tr');

    if (fixedColumnsOptions.leftColumns) {
        const clonedRows = $table.parent().parent().parent().find('.DTFC_LeftBodyWrapper .DTFC_Cloned').find('tbody>tr');
        for (let i = 0; i < rows.length; i++) {
            ko.applyBindings(ko.contextFor(rows[i]), clonedRows[i]);
        }
    }

    if (fixedColumnsOptions.rightColumns) {
        const clonedRows = $table.parent().parent().parent().find('.DTFC_RightBodyWrapper .DTFC_Cloned').find('tbody>tr');
        for (let i = 0; i < rows.length; i++) {
            ko.applyBindings(ko.contextFor(rows[i]), clonedRows[i]);
        }
    }
}

我创建了一个github repo,其中也包含一些示例,因为它可能会帮助其他人。

如果您觉得这个回答了问题,请投票,我会将其标记为答案 - 它肯定会回答我最初的问题,但我会很感激一些反馈。

请注意,这是ES2015代码,因此您需要进行转换。