Knockout.js Validation: Check if value is in range
To validate a model and its properties (observable and ordinary ones) we use such knockout.js plugin as Knockout Validation. This validation library is simple and easy to extend. For example, a new validation rule to check if value is in range could be as simple as the following:
import ko = require("knockout"); import validation = require("knockout.validation"); ... export function enableCustomValidators() { validation.rules['inRange'] = { validator: function (val, params) { var minVal = ko.validation.utils.getValue(params.min); var maxVal = ko.validation.utils.getValue(params.max); var res = params.includeMin ? val >= minVal : val > minVal; if (res) res = params.includeMax ? val <= maxVal : val < maxVal; if (!res) this.message = ko.validation.formatMessage(params.messageTemplate || this.messageTemplate, [minVal, maxVal, (params.includeMin ? '[' : '('), (params.includeMax ? ']' : ')') ]); return res; }, messageTemplate: 'Value has to be in range {2}{0}, {1}{3}' }; validation.registerExtenders(); // adds validation rules as extenders to ko.extenders } ... // the enableCustomValidators should be called when the custom // validation rules are supposed to be used
Note the code above and further is TypeScript. The TypeScript definition to the Knockout Validation is available as a NuGet package, namely knockout.validation.TypeScript.DefinitelyTyped.
The inRange rule accepts the start and end values and allows to indicate should or should not they be included in the range (by default boundaries are excluded). The validation error message is being generated based on the custom defined messageTemplate or default one. The default message template uses four placeholders where the {0} and {1} are start and end values correspondingly. The latter two represent brackets: square or round ones depending on the boundaries inclusion.
The example below defines inRange validation to keep the value between 0 (including it) and 10 (excluding it):
public someNumber = ko.observable<number>(null).extend( { required: { params: true, message: "This field is required" }, number: { params: true, message: "Must be a number" }, inRange: { params: { min: 0, includeMin: true, max: 10, //includeMax: true, } } });
In case of a wrong value the message will says “Value has to be in range [0, 10)“.
To redefine the message template the following might be used:
public somePositiveNumber = ko.observable<number>(null).extend( { required: { params: true, message: "This field is required" }, number: { params: true, message: "Must be a number" }, inRange: { params: { min: 0, max: 1, messageTemplate: "Must be a positive number less than {1}"// the range end value will be put } } });
Note the ko.validation.formatMessage has been used to generate the final validation message. Till recently the ko.validation.formatMessage wasn’t able to process more than one placeholder in template. So, if you use one of such outdated versions of Knockout Validation library, you may be interested in the actual formatting method implementation shown below. It’s borrowed from the same Knockout Validation, but of up-to-date version.
function formatMessage(message, params, observable?) { if (ko.validation.utils.isObject(params) && params.typeAttr) { params = params.value; } if (typeof message === 'function') { return message(params, observable); } var replacements = ko.utils.unwrapObservable(params); if (replacements == null) { replacements = []; } if (!ko.validation.utils.isArray(replacements)) { replacements = [replacements]; } return message.replace(/{(\d+)}/gi, function (match, index) { if (typeof replacements[index] !== 'undefined') { return replacements[index]; } return match; }); }