Search code examples
javascriptknockout.jsdurandalknockout-binding-handlers

KnockoutClassBindingProvider: How to perform a foreach binding


I see the basic example on github but I can't get it to work with my code. I should add that I'm using durandal.

How do I get the bindings to work? Am I doing anything wrong?

Input.js

define(['knockout'], function (ko) {

    var ctor = function (value) {
        //Properties
        this.value = ko.observable(value);
        this.placeholder = 'Input';

        //Methods
        this.getBindings = function () {
            var bindings = {};
            bindings.Input = {
                value: this.value,
                attr: {
                    placeholder: this.placholder,
                },
            };
            bindings.Test = {
                text: this.value,
            };

           return bindings;
        };
    };


    return ctor;
});

Form.js

define(['knockout', 'Input'], function (ko, Input) {

    var ctor = function (inputs) {
        //Properties
        this.inputs = ko.observableArray(inputs);

        //Methods
        this.getBindings = function () {
            var bindings = {};
            bindings.Inputs = {
                foreach: this.inputs,
                Item: function (context, classes) {
                    return context.$data.getBindings();
                },
            };

            return bindings;
        };
    };


    return ctor;
});

Module.js

define(['knockout', 'Input', 'Form'], function (ko, Input, Form) {
    var ctor = function () { };

    ctor.prototype.activate = function () {
        var data = [
            new Input(123),
            new Input("Chris"),
            new Input(true)
        ];
        this.form = new Form(data);
    };

    ctor.prototype.binding = function () {
        var bindings = this.form.getBindings();
        ko.bindingProvider.instance.registerBindings(bindings);
    };


    return ctor;
});

Module.html This does not work.

<div id="Module">
    <div data-class="Inputs">
        <div>
            <input data-class="Inputs.Item.Input" />
            <span data-class="Inputs.Item.Test"></span>
        </div>
    </div>
</div>

Module.html This does work but I'm not using classBindingProvider for the foreach.

<div id="Module">
    <div data-class="Inputs">
        <div>
            <input data-bind="value: value, attr: { placeholder: placeholder }" />
            <span data-bind="text: value"></span>
        </div>
    </div>
</div>

There's no error message but the binding never happens. I just get 3 empty input fields.


Solution

  • I figured it out. I'll post the code that works.

    I changed two things. First, I added <div data-class="Inputs.Item"> and then referenced the properties relative to that location (Input and Test). Second, I register the bindings immediately inside the getBindings functions, which will now turn them into initBindings.

    Input.js

    define(['knockout'], function (ko) {
    
        var ctor = function (value) {
            //Properties
            this.value = ko.observable(value);
            this.placeholder = 'Input';
    
            //Methods
            this.initBindings = function () { //FIX: getBindings => initBindings
                var bindings = {};
                bindings.Input = {
                    value: this.value,
                    attr: {
                        placeholder: this.placholder,
                    },
                };
                bindings.Test = {
                    text: this.value,
                };
    
                ko.bindingProvider.instance.registerBindings(bindings); //FIX: register instead of return
            };
        };
    
        return ctor;
    });
    

    Form.js

    define(['knockout', 'Input'], function (ko, Input) {
    
        var ctor = function (inputs) {
            //Properties
            this.inputs = ko.observableArray(inputs);
    
            //Methods
            this.initBindings = function () { //FIX: getBindings => initBindings
                var bindings = {};
                bindings.Inputs = {
                    foreach: this.inputs,
                    Item: function (context, classes) {
                        context.$data.initBindings(); //FIX: Call the init.
                    },
                };
    
                ko.bindingProvider.instance.registerBindings(bindings); //FIX: register instead of return
            };
        };
    
        return ctor;
    });
    

    Module.js

    define(['knockout', 'Input', 'Form'], function (ko, Input, Form) {
        var ctor = function () { };
    
        ctor.prototype.activate = function () {
            var data = [
                new Input(123),
                new Input("Chris"),
                new Input(true)
            ];
            this.form = new Form(data);
        };
    
        ctor.prototype.binding = function () {
            this.form.initBindings(); //FIX: Call the init.
        };
    
        return ctor;
    });
    

    Module.html

    <div id="Module">
        <div data-class="Inputs">
            <div data-class="Inputs.Item"> //FIX: no binding => Inputs.Item
                <input data-class="Input" /> //FIX: Inputs.Item.Input => Input
                <span data-class="Test"> //Fix: Inputs.Item.Test => Test
                </span> 
            </div>
        </div>
    </div>