Knockoutjs自定义绑定在项目完全呈现之前执行

时间:2013-10-28 16:48:11

标签: knockout.js foreach

我正在尝试使用DataTable对项目列表执行回调以进行分页。现在我想在渲染每个项目之后渲染所有项目之后执行我的回调。在SO question之后我创建了简单的自定义绑定

ko.bindingHandlers.ConvertToDataTable = {
    update: function (element, valueAccessor, allBindings) {
        var list = ko.utils.unwrapObservable(valueAccessor()); //grab a dependency to the obs array        
        var tableId = allBindings().tableId;
        $('#' + tableId).dataTable({
            "sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>"
        });
    }
}

我的观点是:

<table class="table table-striped table-bordered" data-bind="attr: { id: tableId }">
    <thead>
        <tr>
            <td>Task Name</td>
            <td>Task Description</td>
        </tr>
    </thead>
    <tbody>
        <!-- ko foreach: tasks, ConvertToDataTable: tasks, tableId: tableId -->
        <tr>
            <td data-bind="text: Name"></td>
            <td data-bind="text: Details"></td>
        </tr>
        <!-- /ko -->
    </tbody>
</table>

该代码工作正常,但项目数量较少。但是,如果我的物品完全被重新剔除,则需要大量的记录(例如500)自定义绑定。任何想法?

更新
我通过AJAX从服务器获取数据,所以当我的可观察数组items为空时,可能首先执行自定义绑定

2 个答案:

答案 0 :(得分:3)

从逻辑的角度来看,ConvertToDataTable绑定不应该放在桌子本身上,而应放在foreach上吗?

另外,您是不是应该通过绑定或视图模型来控制表格布局?对于硬编码值,自定义绑定是一个非常糟糕的地方。

无论如何,controlsDescendantBindings是你的朋友(docs):

自定义绑定:

ko.bindingHandlers.dataTable = {
    init: function () {
        return { controlsDescendantBindings: true };
    },
    update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        var layout = valueAccessor();

        ko.applyBindingsToDescendants(bindingContext, element);
        $(element).dataTable({ "sDom": layout });
    }
};

查看型号:

{
    dataTableLayout: "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>",
    tasks: ko.observableArray([/* ... */])
}

模板:

<table data-bind="dataTable: dataTableLayout">
    <thead>
        <tr>
            <td>Task Name</td>
            <td>Task Description</td>
        </tr>
    </thead>
    <tbody data-bind="foreach: tasks">
        <tr>
            <td data-bind="text: Name"></td>
            <td data-bind="text: Details"></td>
        </tr>
    </tbody>
</table>

http://jsfiddle.net/WcaM5/

免责声明:我不确切知道jQuery DataTables是如何工作的,因此样本是空中代码。我想说的是,如果需要,你可以手动控制绑定。

答案 1 :(得分:1)

这是我使用的自定义绑定!这包括您要在绑定中定义的所有数据表选项...

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 };
}
};

JSFIDDLE