可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm using Knockout.Validation and I'd like to be able to display an error summary where each line displays the error message (obviously!) and also the field name on the view model related to it, e.g.
- Age - please enter a number
- Date of birth - please enter a proper date
So far I've got a validatedObservable wrapping my view model, and this puts an errors array on my viewmodel automatically, containing all my errors. However I can't see any easy way to retrieve which field each error relates to.
I know I could traverse the view model myself, building up an errors collection of my own from the isValid property - is this my only option though?
Once I have the field name, I can map my validation summary to the related "friendly" label for that field (e.g. "Date of birth" rather than "DateOfBirth").
Here's a simplified version of the code I have so far:
ViewModel
function PersonModel(){
var self = this;
self.Age = ko.observable().extend({ number: true});
self.DateOfBirth = ko.observable({ date: true});
self.Validate = function() {
if (!self.isValid()) {
self.errors.showAllMessages();
return false;
}
return true;
};
ko.validation.init({
grouping: { deep: true, observable: true },
registerExtenders: true,
messagesOnModified: true,
insertMessages: true
});
ko.applyBindings(ko.validatedObservable(new PersonModel()));
Html
<ul data-bind='foreach: model().errors' class="message">
<li>
<span data-bind='text: $data'></span>
</li>
</ul>
Many Thanks
回答1:
You can use custom validation message for any variable.
emailAddress: ko.observable().extend({
required: { message: 'Email Address: Required field.' }
}),
回答2:
You can do the following:
- Add a
friendlyName
extender to provide a friendly name to your observables
- Hack the binding handler that shows the messages
Friendly Name:
Simplest knockout extender ever:
ko.extenders.friendlyName = function (obs, options) {
obs.friendlyName = options;
}
Usage: self.Age = ko.observable(3).extend({ friendlyName: "My Age" });
Display message hack:
Knockout validation plugin creates a bindinghandler validationMessage
to display error messages. You can use it directly (with some html markup) or let the plugin handle the messages with the configuration option insertMessages
.
Here I just edit the html it creates (the original binding handler is still called) to take the friendlyName
into account:
var originalValidationMessageUpdate= ko.bindingHandlers.validationMessage.update;
ko.bindingHandlers.validationMessage.update =
function (element, valueAccessor, allBindingAccessor, viewModel,
bindingContext) {
if (originalValidationMessageUpdate)
originalValidationMessageUpdate(element, valueAccessor,
allBindingAccessor, viewModel,
bindingContext);
var prepend = (ko.isObservable(valueAccessor())
&& valueAccessor().friendlyName)
? valueAccessor().friendlyName + " - "
: "";
$(element).html(prepend + $(element).html());
// ^^^^ This is the line that is actually useful
}
Note: I did not create friendlyName
as an observable since I guess it will not be updated, but it is possible to do it.
Demo
回答3:
Unfortunately you will need to specify messages for each validator unless you do something entirely custom such as GôTô has suggested.
Think about it... you haven't specified a display name for you fields so how is ko validation supposed to know a field name for the validation error message?
The simplest way:
self.Age = ko.observable().extend({
number: {
params: true,
message: 'Age - please enter a number'
},
required: {
params: true,
message: 'Age is required'
}
});
If it justifies the effort, you may want to invest in extending the default messaging behaviour else its easier to just specify the messages for each validator on each property.
回答4:
I have found this works well - type it in the console (F12):
for(var propertyName in PersonModel()) {
console.log(ko.validation.group(PersonModel()[propertyName])())
}
This will give you something like:
[]
[]
[]
[This field is required.]
[]
[]
You can then tie up the problem field to the property in your model.
In this example, the problem would be the 4th property.
I hope this helps someone.
回答5:
This minimal overhead approach is ideal for my needs:
ko.validation.rules["required"].message = "{0} required";
usage:
self.driverName = ko.observable().extend({ required: "Driver Name" });
rendered result:
Driver Name required
Note: stepping through knockout.validation.js showed me it's crucial to modify the default ko.validation.rules[] PRIOR to any ko.observable().extend() calls.
回答6:
While this doesn't answer the "friendly" label portion of this question, nor does it result in an array of validation errors, it does build up an object to let you see where in a huge viewmodel validation errors are hiding. The question mentions traversing through the viewmodel to build up the errors, so I figured I'd share the recursive function I built for doing that with some help from Lodash.
function getErrors(koVm, maxDepth, currentDepth) {
if ((currentDepth || 0) < (maxDepth || 5)) {
var obj = _.pickBy(_.mapValues(koVm, function(value) {
if (value) {
if (_.isFunction(value.error)) {
return value.error();
}
return getErrors(value, maxDepth, (currentDepth || 0) + 1);
}
return null;
}));
return _.keysIn(obj).length ? obj : null;
}
return null;
}
It's not great for showing errors to users (unless you flatten the constructed object), but it's been a great help when debugging and trying to figure out what part of a huge model isn't valid.