Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to raise an event after knockout rendering is complete?

This jquery mobile table is being rendered with knockout.

<table data-role="table" id="report-table" class="ui-body-a ui-shadow ui-responsive table-stripe"
    data-column-btn-theme="a" data-column-btn-text="Spalten..." data-column-popup-theme="a" data-mode="columntoggle"">
    <thead>
        <tr data-bind="foreach: columns">
            <th data-bind="text: $data.Caption, attr: { 'data-priority': 1 + Math.floor($index() / 4) }"></th>
        </tr>
    </thead>
    <tbody data-bind="foreach: { data: rows, afterRender: tableRowAfterRender }">
        <tr data-bind="foreach: $parent.columns">
            <!-- ko template: { name: $data.template } -->
            <!-- /ko -->
        </tr>
    </tbody>
</table>

To get the "columntoggle" to actually work, I currently use the "aferRender" event:

self.tableRowAfterRender = function (element, data) {
    // Skip unless data.Items()[i] is not the last element in the rows collections
    for (var i = 0; i < data.Items().length - 1; i++) {
        if (data.Items()[i] !== self.rows()[self.rows().length - 1].Items()[i])
            return;
    }

    // refresh table after 100ms delay
    setTimeout(function () { $("#report-table").table("refresh"); }, 100);
}

This is shaky, I hate the setTimeout() way of doing things, and this situation became quiet common for me with jquery mobile and knockout. I need a robust way to raise an event once all the knockout rendering or ideally once all the rendering concerned with elements inside the table-element is done. I was able to use custom bindings in some such situations, but I would not know how to do this here.

like image 849
TvdH Avatar asked Apr 11 '14 13:04

TvdH


2 Answers

Try this. Wrap the table with: <div data-bind='template: { afterRender: myPostProcessingLogic }'>. Then do whatever you need to do in myPostProsssingLogic. This will only be called when the table is first rendered however. Here's a fiddle:

 <div data-bind='template: { afterRender: myPostProcessingLogic }'> 
<table data-role="table" id="report-table" class="ui-body-a ui-shadow ui-responsive table-stripe"
    data-column-btn-theme="a" data-column-btn-text="Spalten..." data-column-popup-theme="a" data-mode="columntoggle"">
    <thead>
        <tr data-bind="foreach: columns">
            <th data-bind="text: $data.Caption, attr: { 'data-priority': 1 + Math.floor($index() / 4) }"></th>
        </tr>
    </thead>
    <tbody data-bind="foreach: { data: rows, afterRender: tableRowAfterRender }">
        <tr data-bind="foreach: $parent.columns">
            <!-- ko template: { name: $data.template } -->
            <!-- /ko -->
        </tr>
    </tbody>
</table>

like image 147
Chuck Schneider Avatar answered Sep 19 '22 14:09

Chuck Schneider


You can try to use binding init

ko.bindingHandlers.init = {
    init: function(element, valueAccessor, allBindings, viewModel) {
        var action = valueAccessor();
        if (typeof action === 'function') {
            setTimeout(action.bind(viewModel, element), 0);
        }
    }
};

Like this:

ko.components.register('simple-component', {
    viewModel: function(params) {        
        this.title = params.title;
        this.initHandler = function() {
            console.log('DOM of component has been built.');
        };
    },
    template:
        '<div data-bind="init: initHandler">\
            Title is: <span data-bind="text: title"></span>\
        </div>'
});

Information for more complex cases is here - http://blog.atott.ru/2015/08/dom-built-event-for-knockout-components.html

like image 37
atott Avatar answered Sep 21 '22 14:09

atott