javascriptjqueryasp.net-mvc-4knockout.jsmvccontrib-grid

knockout apply binding to dynamically loaded html from ajax mvc contrib grid


I have an AJAX MVC Contrib Grid implementation, that already existed and now I am in a situation where I am trying to bolt on some knockout functionality... and I want to know if this is possible without changing the whole grid implementation.

This is the refresh grid function that is setting the container html when the pagination changes.

scope.refreshGrid = function (container, url) {
    if (url)
        container.data(scope.selectors.actionUrlAttribute, url);
    $.post((url || container.data(scope.selectors.actionUrlAttribute)), scope.getParams(),
        function(html) {
            container.html($(html).html());
            scope.bindDeleteButtons();
        }).done(function() {
            container.trigger("refresh.ctb.grid");
        });
}

one of the columns for the grid is custom column that uses Html.Partial like this:

column.Custom(x => Html.Partial("_CartSelection", new CartSelection(x.Id))); 

The partial view has the below markup with some knockout data bindings

<input type="checkbox" value="@Model.Id" data-bind="enable: (selectionEnabled() || $element.checked), checked: selectionIds" />

This works for the first page of results, when the paging is selected to change the page and the container html() is updated the bindings no longer work but the KO viewModel still has the correct selectionIds.. which is what I was expecting to happen.

The KO view model is being applied as shown below, where the grid has a wrapper parent div with an id of "cart":

    $(function() {
        var viewModel = new IP.Configuration.CartSelector(new IP.Router());
        ko.applyBindings(viewModel, document.getElementById("cart"));
    });

I have already seen comments in other posts about how you shouldn't re-apply bindings. In my case it seems I want to apply bindings but only to some child nodes that are being dynamically loaded.

Is this possible?

UPDATE: Almost had this working by adding a cart-selection class to each checkbox and doing the below in a rebind function on the viewModel, where self is the viewModel:

        $("#cart .cart-selection").each(function(index, item) {
            ko.applyBindings(self, item);
        });

Then doing the below on the custom trigger for refreshing the grid, when the content is reloaded.

        $("#cartGrid").on("refresh.ctb.grid", function() {
            viewModel.rebind();
        });

The issue I am finding with this at the moment is that the checkboxes are no longer enabled regardless of the $element.checked binding.. maybe a valueHasMutated will fix this, still looking into this.


Solution

  • I figured out what my remaining problem was, it was due to the ordering of the data bindings.

    The enable data bind needed to be placed after the checked binding since it has a dependency on it via $element.checked which makes sense now after realising it!!

    I changed my rebind function slightly to the below:

    var gridResult = $("#cartGrid table");
    if (gridResult.length > 0)
        ko.applyBindings(this, gridResult[0]);
    

    Each refresh brings in a new table but at least now if I add any more bindings to other elements in the results from the grid, they will work as expected.