I am trying to experiment and see if there are any clever solutions for creating custom validators that can be abstracted for neatness and reuse.
In the jsfiddle below, I just put together a simple parent model that stores and array of Measurements (just a value and a date). In this example, I put in two requirements.
- Each Measurement has either both fields supplied, or neither must be supplied.
There has to be at least one valid (meets previous condition) measurement in parent array.
Ideally, I want the validation logic for what defines being valid, stored within the Measurement object as I have done below. But, the thing I am very distastful of is the "hands-on" validation I am having to perform in the parent model in
atLeastOne()
.
Knockout validation will automatically validate the individual fields for numbers and dates BUT, I have to step in and perform validation against the rule for the array.
Question: Are the any approaches that allow me to setup KO validation to check the array for this the required condition while still having the HasValues
method still reside in the Measurement Model??? I.e I want to abstract the concept of searching for "at least one" into a custom validator of some sort that can handle the job for me, and then just tell this validator "hey, here is the function I want you to use to validate each item in the array."
Thanks in advance!
function Model(data)
{
var self = this;
self.Measurements = ko.observableArray();
for(var i = 0; i < data.length; i++)
self.Measurements.push(new Measurement(data[i]));
function hasAtLeastOne(){
var atLeastOne = false;
$.each(self.Measurements(), function(i, item) {
if (item.HasValues()) {
atLeastOne = true;
return;
}
});
return atLeastOne;
}
self.Save = function() {
if (self.canSave() && atLeastOne())
alert('save');
else
alert('arg!');
};
self.errors = ko.validation.group(self);
self.canSave = ko.computed(function() {
return self.errors().length == 0;
});
}
function Measurement(data)
{
var self = this;
self.Value = ko.observable(data.val);
self.Date = ko.observable(data.date);
self.Value.extend({ required: { onlyIf: isRequired }, number: true });
self.Date.extend({ required: { onlyIf: isRequired }, date: true });
self.HasValues = function() {
return ko.utils.isNotNullUndefOrEmpty(self.Value()) &&
self.Date() && self.Date().length > 0;
};
function isRequired() {
return ko.utils.isNotNullUndefOrEmpty(self.Value()) ||
(self.Date() && self.Date().length > 0);
}
}
ko.utils.isNotNullUndefOrEmpty = function (value) {
return (typeof value === 'string' && value.length > 0) ||
(typeof value !== 'string' && value);
};
Here is a jsfiddle to play with that has my example: http://jsfiddle.net/cACZ9/