Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tie together ko.validation errors with related viewmodel field names

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

like image 564
philicomus Avatar asked Aug 08 '13 15:08

philicomus


3 Answers

You can use custom validation message for any variable.

emailAddress: ko.observable().extend({
    required: { message: 'Email Address: Required field.' }
}),
like image 53
amakhrov Avatar answered Nov 18 '22 14:11

amakhrov


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

like image 7
GôTô Avatar answered Nov 18 '22 15:11

GôTô


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.

like image 3
Sir Crusher Avatar answered Nov 18 '22 14:11

Sir Crusher