JSFiddle - React, Tailwind, and code Playground
HTML
<script type="text/html" id="validation">
<span data-bind="foreach: $data" class="errors">
<span data-bind='text: $data'> </span>
</span>
</script>
<p>
First name:
<input data-bind='valueWithValidation: firstName' />
</p>
<p>
Last name:
<input data-bind='valueWithValidation: lastName' />
</p>
<p>
Last name:
<input data-bind='valueWithValidation: phoneNr' />
</p>
<p data-bind="text: errorSummary, visible: errorSummary()" class="errors"></p>
CSS
.errors {
color: red;
}
JavaScript
ko.bindingHandlers.valueWithValidation = {
init: function(element, valueAccessor, allBindingsAccessor) {
// Interception! Add validation markup to the DOM and
// apply the template binding to it. Some of this code
// can be more elegant, especially if you use jQuery or
// a similar library.
var validationElement = document.createElement("span");
element.parentNode.insertBefore(validationElement, element.nextSibling);
ko.applyBindingsToNode(validationElement, {
template: {
name: 'validation',
data: valueAccessor().errors
}
});
// The rest of this binding is handled by the default
// value binding. Pass it on!
ko.applyBindingsToNode(element, {
value: valueAccessor(),
valueUpdate: 'afterkeydown'
});
}
};
ko.extenders.regex = function(target, options) {
// Default options
options = options || {};
var regexp = new RegExp(options.pattern || ".*");
var message = options.message || "regex is mad at you, bro!";
// Only create sub-observable if it hasn't been created yet
target.errors = target.errors || ko.observableArray();
function validate(newValue) {
var matched = regexp.test(newValue);
if (!matched && target.errors.indexOf(message) == -1) {
target.errors.push(message);
} else if (matched && target.errors.indexOf(message) >= 0) {
// TODO: support multiple extender instances with same message yet different pattern
target.errors.remove(message);
}
}
//initial validation
validate(target());
//validate whenever the value changes
target.subscribe(validate);
//return the original observable
return target;
};
function AppViewModel(first, last, phone) {
// These example regexes are silly, but wonderful for demo purposes :-)
var self = this;
self.firstName = ko.observable(first);
self.firstName = self.firstName.extend({
regex: {
pattern: "^[^0-9]*$",
message: "No digits plz!"
}
})
...