williamparry
williamparry

Reputation: 488

Knockout Custom Binding foreach

I'm trying to create a validation summary that has list of links to the errors for the KnockoutJS Validation Library and I need to create a custom foreach handler to present it.

With the code below what I'm trying to achieve is ability to listen to a change in the errors() list and construct a validation summary. I don't actually use the "errors" list (since it's just a list of strings) but use it to listen to changes.

From what I've looked at (somewhat related) I have the scope wrong - as in "ko.applyBindingsToDescendants" expects the ViewModel of the child element, but I'm unsure of how to implement this.

This is my current binding code:

ko.bindingHandlers.validationSummary = {

    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

        function ValidationErrorModel(fieldRef, errorMessage) {
            this.fieldRef = ko.observable(fieldRef),
            this.errorMessage = ko.observable(errorMessage)
        }

        var currentValidationModel = ko.observableArray(),
            $validationElements = $(".validationElement");

        $validationElements.each(function (i, elem) {

            var $elem = $(elem),
                validationErrorModel = new ValidationErrorModel("#" + $elem.attr("name"), $elem.attr("title"));

            currentValidationModel.push(validationErrorModel);

        });

        console.log(currentValidationModel());
        ko.applyBindingsToDescendants({ foreach: currentValidationModel }, element);


        return { controlsDescendantBindings: true };
    },

    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

    }
};

And my template:

<ul data-bind="validationSummary: errors">
    <li>
        <a data-bind="attr: { href: fieldRef }, text: errorMessage"></a>
    </li>
</ul>

Upvotes: 2

Views: 2801

Answers (1)

williamparry
williamparry

Reputation: 488

Stackoverflow user @antishok helped me with this on the #knockoutjs IRC channel:

The code needs a setTimeout because the elements are only updated with the class and attributes after the sucriptions to errors are fired

Custom binding:

ko.bindingHandlers.validationSummary = {

    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        return ko.bindingHandlers.foreach.init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
    },

    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

        var value = valueAccessor();
        value(); // register dependency

        function ValidationErrorModel(fieldRef, errorMessage) {
            this.fieldRef = ko.observable(fieldRef),
            this.errorMessage = ko.observable(errorMessage)
        }

        setTimeout(function() {
            var currentValidationModel = 
                $(".validationElement").map(function(i, elem) {
                    var $elem = $(elem);
                    return new ValidationErrorModel("#" + $elem.attr("name"), $elem.attr("title"));
                }).get();

            ko.bindingHandlers.foreach.update(element, function() { return currentValidationModel }, allBindingsAccessor, viewModel, bindingContext);
        }, 0);
    }
};

HTML:

<ul data-bind="validationSummary: errors">
    <li>
        <a data-bind="attr: { href: fieldRef }, text: errorMessage"></a>
    </li>
</ul>

Upvotes: 2

Related Questions