I have a simple view-model with a few required attributes... I want each input to highlight red if the corresponding property is not valid, but I don't want this highlighting to display when the page is initially loaded... only when a value changes or when the user tries to save / continue...
Right now it's validating the view-model on initial load because I'm specifying data-bind="css: { error: name.isValid() == false }", but I don't know of any other way to get this to work dynamically (similar to how jQuery unobtrusive validation works)...
var foo = { name: ko.observable().extend({required: true}) };
<div data-bind="css: { error: !name.isValid() }">
<input type="text" data-bind="value: name" />
</div>
Any ideas on how to make this work would be appreciated... Thanks!
A better approach is to configure knockout validation to decorate the element with the validationElement class. This is done by adding this configuration option:
ko.validation.configure({ decorateElement: true });
Click here to see a jsfiddle demonstrating this.
****EDIT, IN RESPONSE TO COMMENT FROM QUESTION ASKER***
If you need to decorate the parent element, a more elegant and reusable solution is to apply this custom binding to the parent element.
Javascript
ko.bindingHandlers.parentvalElement = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var valueIsValid = valueAccessor().isValid();
if(!valueIsValid && viewModel.isAnyMessageShown()) {
$(element).addClass("parentError");
}
else {
$(element).removeClass("parentError");
}
}
};
And apply the binding in your HTML like so:
<form data-bind='submit:OnSubmit'>
<label data-bind='parentvalElement:name'>
<span>Name</span>
<input data-bind="value: name" />
</label>
<input type='submit' value='submit' />
<form>
Take a look at this updated jsfiddle to see it in action.
So, here is the solution I came up with:
var Foo = function()
{
this.name = ko.observable().extend({required: true}).isModified(false);
this.validate: function()
{
if (!this.isValid())
{
//... loop through all validated properties and set .isModified(true)
return false;
}
return true;
};
ko.validation.group(foo);
};
var Bar = function()
{
this.foo = new Foo();
this.errors = ko.observableArray([]); //<-- displays errors for entire page
this.save = function()
{
if (!this.foo.validate())
{
this.errors(ko.toJS(this.foo.errors()));
}
};
}
ko.applyBindings(new Bar());
And here is the markup...
<div data-bind="with: foo">
<div class="control-group"
data-bind="css: { error: name.isModified() && !name.isValid() }">
<label class="control-label">Name<span class="help-inline">*</span></label>
<div class="controls">
<input type="text" class="input-block-level" placeholder="Name"
data-bind="value: name, event: { blur: function () { name.isModified(true); }}" />
</div>
</div>
<div class="alert alert-error"
data-bind="visible: $parent.errors().length > 0">
<h5>Errors!</h5>
<ul data-bind="foreach: $parent.errors()">
<li data-bind="text: $data"></li>
</ul>
</div>
</div>
<button type="submit" class="btn btn-primary" data-bind="click: save">Save</button>
and here is the CSS
.error { color: Red; font-weight: bold; }
.help-inline { display: none; }
.error .help-inline { display: inline-block; }
.error input { border-color: Red; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With