TvdH
TvdH

Reputation: 1218

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.

Upvotes: 7

Views: 7430

Answers (3)

atott
atott

Reputation: 890

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

Upvotes: 2

Muhammad Raheel
Muhammad Raheel

Reputation: 19882

There is a simple way of doing it. Here is the process

Assign a class to your row

<tr class="row" data-bind="foreach: $parent.columns">

Check in your function with if condition

if(data.Items().length == $('row').length){
    $("#report-table").table("refresh")
}

Or call any event manually

$('#report-table').trigger('click')

Hope that's helpful to you.

EDITS:

Create a new observable and set it to false.

self.EnableTimeOut = ko.observable(false)

Now set it true according to your requirement. This is only example

if(data.Items().length == $('row').length && data.Items().length > 0){
    self.EnableTimeOut(true)
    $("#report-table").table("refresh")
}

And finally

Wrap it in condition

setTimeout(function () { 
    if(self.EnableTimeOut()){
        $("#report-table").table("refresh"); 
    }
}, 100);

Upvotes: 0

Chuck Schneider
Chuck Schneider

Reputation: 344

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>

Upvotes: 3

Related Questions