Reputation: 488
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
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