Knockout.js - Form validation and reset to last valid value
by nitronic
HTML
<script src="https://github.com/SteveSanderson/knockout/raw/master/build/output/knockout-latest.js"></script>
<script src="https://github.com/downloads/SteveSanderson/knockout/jquery.tmpl.js"></script>
<div id="bookform">
<h3>Title</h3>
<p data-bind="css: { error: title.hasError }">
<input data-bind='value: title, valueUpdate: "afterkeydown", event: { blur: function() { updateLastValidValue(title); }}' />
<span data-bind='visible: title.hasError, text: title.validationMessage'> </span>
</p>
<h3>Author</h3>
<p data-bind="css: { error: author.hasError }">
<input data-bind='value: author, valueUpdate: "afterkeydown", event: { blur: function() { updateLastValidValue(author); }}' />
<span data-bind='visible: author.hasError, text: author.validationMessage'> </span>
</p>
<h3>Pages</h3>
<p data-bind="css: { error: pages.hasError }">
<input data-bind='value: pages, valueUpdate: "afterkeydown", event: { blur: function() { updateLastValidValue(pages); }}' />
<span data-bind='visible: pages.hasError, text: pages.validationMessage'> </span>
</p>
<p>
<input type="button" value="Save" data-bind="disable: hasErrors"></input>
<input type="button" value="Undo" data-bind="enable: hasErrors, click: resetToValidValues"></input>
</p>
</div>
CSS
h3 {
font-size: 120%;
font-weight: bold;
margin-top: 5px;
}
.error {
color: red;
}
#bookform {
margin: 10px;
padding: 20px 20px 25px 20px;
background-color: #eee;
}
input {
width: 250px;
}
JavaScript
ko.extenders.requiredField = function(target, message) {
target.hasError = ko.observable();
target.validationMessage = ko.observable();
target.lastValidValue = ko.observable();
function validate(newValue) {
target.hasError(newValue ? false : true);
target.validationMessage(newValue ? "" : message || "* required");
}
function setInitialValueIfValid() {
if (!target.hasError()) {
target.lastValidValue(target());
}
}
validate(target());
setInitialValueIfValid();
target.subscribe(validate);
return target;
};
function BookViewModel(title, author, pages) {
var self = this;
self.title = ko.observable(title).extend({ requiredField: "Please enter a title" });
self.author = ko.observable(author).extend({ requiredField: "Please enter an author" });
self.pages = ko.observable(pages).extend({ requiredField: "" });
self.hasErrors = ko.computed(function() { return self.title.hasError() || self.author.hasError() || self.pages.hasError(); });
self.updateLastValidValue = function(observable) {
if (!observable.hasError()) {
observable.lastValidValue(observable());
}
};
self.resetToValidValues = function() {
self.title(self.title.lastValidValue());
self.author(self.author.lastValidValue());
self.pages(self.pages.lastValidValue());
};
}
var viewModel = new BookViewModel("Windows Presentation Foundation 4.5 - EinfĂĽhrung und Praxis", "Norbert Eder", 400);
ko.applyBindings(viewModel);