0
votes

Using the following custom binding I am receiving the following and I can not figure out why.

Version Knockout JavaScript library v3.2.0

You cannot apply bindings multiple times to the same element.

Binding

ko.bindingHandlers.foreachArraySplice = {
    init: function (element, valueAccessor, allBindings) {
        var value = ko.unwrap(valueAccessor());
        var columns = allBindings.get('columns') || 1;

        /* Split the array into splices */
        var len = value.length;
        var splitArray = [];
        var i = 0;
        while (i < len) {
            var size = Math.ceil((len - i) / columns--);
            splitArray.push(value.slice(i, i + size));
            i += size;
        }
        /*\Split the array into splices */
        ko.cleanNode(element); // Last attempted fix...
        ko.applyBindingsToNode(element, {
            foreach: splitArray
        });
    }
};

Usage

<div class="row" data-bind="foreachArraySplice: anArray, columns: 5">
  <div class="col-md-2" data-bind="foreach: $data, css: { first: $index == 0, last: $index == $data.length }">
    <div class="input-group" style="margin-bottom: 5px;">
      <input class="form-control input-sm" type="text" data-bind="value: $data.key" readonly>
    </div>
  </div>
</div>

Reasoning

In a nutshell I need to take an array of objects (100s) and display them on a page. I use bootstrap so I figured I would make a binding handler that took the array and splice it into smaller arrays within the array based on the columns binding, and display them neatly in my view as shown above.

The arrays always vary so I wanted it to be dynamic as possible, but somewhere the code above is causing the aforementioned error. For brevity I did not include my viewModel as it is a standard model with an observable array containing objects.

1

1 Answers

2
votes

You need to tell knockout that your binding handler will control the bindings of the descendant elements. They are effectively being processed twice by knockout, from your call to applyBindingsToNode() and through normal knockout processing.

You do so by making your init() function return an object with a controlsDescendantBindings property set to true. More on that in the documentation.

ko.bindingHandlers.foreachArraySplice = {
    init: function (element, valueAccessor, allBindings) {
        // ...
        return { controlsDescendantBindings: true };
    }
};