validation
validation copied to clipboard
Dynamic validation error messages
I'm submitting a feature request
Currently, it is impossible to create a dynamic validation error message. For example, I want to validate IBAN account number. I configure the rule as follows:
ValidationRules
.ensure('account').displayName("Account number")
.satisfies(account => getActualChecksum(account) == calculateChecksum(account))
.withMessage("Given IBAN number is invalid");
The validation message could be much more informative, for example Given IBAN number has cheksum 34 but it should be 56
.
What about allowing to pass a callback that will generate a validation message based on given value? This should solve all of the cases where the validation message should be built based on the given value or something else dynamic.
ValidationRules
.ensure('account').displayName("Account number")
.satisfies(account => getActualChecksum(account) == calculateChecksum(account))
.withMessage(account => "Given IBAN has checksum " + getActualChecksum(account) + " but it should be " +calculateChecksum(account));
OR it should be possible to define special parameters for message in addition to existing $displayName
and $value
, e.g.
ValidationRules
.ensure('account').displayName("Account number")
.satisfies(account => getActualChecksum(account) == calculateChecksum(account))
.withMessage("Given IBAN has checksum ${actualChecksum} but it should be ${correctChecksum}")
.withMessageParamsBuilder(account => {actualChecksum: getActualChecksum(account), correctChecksum: calculateChecksum(account)});
What do you think?
you could do this:
.withMessage("Given IBAN number has checksum ${calculateChecksum($value)} but it should be ${getActualChecksum($value)}");
How should that work? Where should I define the getActualChecksum
and calculateChecksum
methods so they are corretly resolved? They are not global. In the above example they have been imported to the file I defined the rules in, e.g. import {getActualChecksum} from "./iban-checksum"
. Writing the message as you suggested results in empty replacements, no errors in console.
Ok, you have pushed me into the right direction. You can pass the functions into the rule config, i.e.
ValidationRules
.ensure('account').displayName("Account number")
.satisfies(account => getActualChecksum(account) == calculateChecksum(account), {getActualChecksum, calculateChecksum})
.withMessage("Given IBAN number has checksum ${$config.calculateChecksum($value)} but it should be ${$config.getActualChecksum($value)}");
and it works, indeed.
However, I think this is an abuse of what rule's config has been designed for.
And it does not solve the situation when an async function is used in satisfies
and the backend responds with custom replacements. I mean: backend validation not only tells that the input is wrong but also what is wrong with it.
Note that if the thing you're trying to compute dynamically is in the object the rules apply to you can use this:
.withMessage('Something or other: ${$object.thePropertyNeeded}')
There should be something like $validationContext
passed to any validation rule/function. Element, object, value, $config,...
should be moved somehow under the $validationContext
instance.
Then, $validationContext
would be "the truest" place to get "a dynamic message" to be rendered, since it could be constructed by a validation function.
Now, the validation code works with arguments , instead of single argument in the form of $validationContext
instance, extensible & configurable e.g. by a 'validation context factory'.
FYI this requirement is addressed in Aurelia 2. In Aurelia 2 you can create your own custom rule class. This gives the encapsulation, as @PetrMotlicek suggests. Every rule has an execute
method, which can be used to set any rule instance properties. In addition, the message template supports accessing the rule instance properties using $rule.property
syntax. For more info, refer https://docs.aurelia.io/app-basics/validating-data.