Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use custom binding with ko.observableArray()

How does knockout custom binding work with observableArray? When using ko.observable() with custom binding, everything works as expected. When using ko.observableArray(), only the initial events (init and update once) are thrown, but further changes are not detected (see Fiddle or code below).

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="knockout-2.2.1.js"> </script>
</head>
<body>
    <div data-bind="text: Observable, updateBinding: Observable"></div>
    <div data-bind="foreach: ObservableArray, updateBinding: ObservableArray">
        <span data-bind="text: $data"></span>
    </div>
    <script type="text/javascript"> 
        ko.bindingHandlers['updateBinding'] = {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                alert("Binding Handler (Init)");
            },
            update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                alert("Binding Handler (Update)");
            }
        };

        function ViewModel() {
            var self = this;

            self.ObservableArray = ko.observableArray();
            self.Observable = ko.observable();
        }

        var viewModel = new ViewModel();

        // Fires Init + Update for Observable and ObservableArray
        ko.applyBindings(viewModel);

        // Fires Update for Observable
        viewModel.Observable(1);

        // Does nothing
        viewModel.ObservableArray.push('1');
    </script>
</body>
</html>
like image 382
Dresel Avatar asked Feb 05 '13 19:02

Dresel


1 Answers

You will want to create a dependency on your observableArray within your custom binding. So, at the very least something like:

    ko.bindingHandlers.updateBinding = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            alert("Binding Handler (Init)");
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            //create a dependency, normally you would do something with 'data'
            var data = ko.utils.unwrapObservable(valueAccessor());
            alert("Binding Handler (Update)");
        }
    };

The reason that this works with your observable example is because all bindings on a single element are triggered together (more info here).

The reason that this does not behave the same way on your other binding is that foreach behaves differently. Changes to the observableArray do not trigger the foreach binding directly (or the whole section would be re-rendered). Instead it triggers logic in a separate ko.computed that evaluates how the array has changed and makes the necessary incremental updates (add an item, remove an item, etc.).

like image 90
RP Niemeyer Avatar answered Oct 26 '22 13:10

RP Niemeyer