'knockout validator with dynamic message

I want my knockout validator to have a message that depends on the validation of the input. It seems like a very common use case but I can't find any way of doing it... here's a simplistic example of what I'd like to do

ko.validation.rules.dumb = {
    validator: function( value )
    {
       if (value.startsWith( "s")) return {isValid:true}
       return {isValid:false, message: value + " needs to start with an s"}
    }
}

some_field.extend({dumb: {}});

This sort of works:

ko.validation.rules.sort_of_works = {
    validator: function( value)
    {
       if (value.startWith("s")) return true;
       ko.validation.rules.message = value + needs to start with an s";
       return false;
    }
}

but it really doesn't - because it only works if you only have one field using that validator :(

I tried accessing "this" in the function, but the this is the this of the validator function - which isn't useful, as it doesn't have a message on it. Also - I've seen people make message a function, so it depends on the input itself - but my validation is expensive (think something like parsing, where you want to say exactly where the error is in the string) - and I don't want to do it once for the validation, and then again for the message.

What I want works perfectly with the async validation callback function - in fact that's sort of what I'm mimicking, the validation actually happens on the server - but unfortunately the rest of the app (not written by me) is not really setup to support IsValidating - so I can't use async.



Solution 1:[1]

I think the library is designed to chain multiple validations using extend.

So, instead of having one validator function that checks a ton of cases, you create a set of validators that all do one simple check that corresponds to a single error message:

ko.validation.rules.startsWith = {
    validator: (val, param) => val?.startsWith(param),
    message: 'The field must start with {0}'
};

ko.validation.rules.endsWith = {
    validator: (val, param) => val?.endsWith(param),
    message: 'The field must end with {0}'
};

ko.validation.registerExtenders();

const myValue = ko.observable()
  .extend({ startsWith: "s" })
  .extend({ endsWith: "p" });

ko.applyBindings({ myValue });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.4/knockout.validation.js"></script>

<label>
  Input a word that starts with an <code>s</code> and ends with a   <code>p</code><br>
<input data-bind="value: myValue">
</label>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 user3297291