How to use same Custom Validation Attribute mutiple times on a property with client side unobtrusive validation in mvc
A week ago, I had a task about checking one
date field [field1] with another date field [field2] and make sure that field1
is earlier than field2, we already have had datecompare custom validation attribute
and it was supposed to be an easy one and take a tick to add this validation!
When I looked at Model ,I noticed that
field1 already has datecompare attribute and it has been checked with other
field [field3] and it checks field1 is after field3.
So I decided to set AllowMultiple
= true in attribute usage of my customattribute(datecompareattribute) and
override TypeId
AllowMultiple
= true in AttributeUsage
Allow Multiple Gets or sets a Boolean value indicating whether more than one instance of the indicated attribute can be specified for a single program element.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
|
Override TypeId in attribute
private object _typeId = new
object();
public override object
TypeId
{
get { return
_typeId; }
}
|
I said to myself how smart I am ;) but
after running project I got this bug
Validation
type names in unobtrusive client validation rules must be unique. The
following validation type was seen more than once
[InvalidOperationException:
Validation type names in unobtrusive client validation rules must be unique.
The following validation type was seen more than once: ....]
|
with a quick research I found a very
impressive post(http://www.codeproject.com/KB/validation/MultipleDataAnnotations.aspx)
about it. Thanks to the author for sharing that. Everything is fine and you can use it freely. except three issues which it has and I’m
describing now.
1- It only can accept 26 same attribute
on a field.
It adds ‘a’ to ‘z’ at the end of attribute name to make it
unique. I think it is alright but if you are crazy enough to have more than 26
same attribute on a field so you need to change a way to create unique id for
each attribute, go for it and then share your way for us :D
2-Remove static when you defining countPerField!
//To avoid multiple rules with same name
public
|
Special
thanks to one of our tech leads who found this!
comment out below part in htmlhelper
adding an extra property (uniquesuffix) in your attribute instead of generating unique id so remove this part to
and only keep below part plus passing your new property
comment out below part in htmlhelper
string
element = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(
ExpressionHelper.GetExpressionText(expression));
string Key =
html.ViewData.Model.ToString() + "." + element;
RequiredIfAttribute.countPerField.Remove(Key);
if
(RequiredIfAttribute.countPerField.Count == 0)
RequiredIfAttribute.countPerField
= null;
|
adding an extra property (uniquesuffix) in your attribute instead of generating unique id so remove this part to
int count = 0; string Key =
metadata.ContainerType.FullName + "." + metadata.GetDisplayName();
if(countPerField==null)
countPerField = new Dictionary<string, int>();
if (countPerField.ContainsKey(Key)) {
count = ++countPerField[Key];
}
else countPerField.Add(Key, count);
|
and only keep below part plus passing your new property
yield return new RequiredIfValidationRule (string.Format(ErrorMessageString,
metadata.GetDisplayName()),requiredFieldValue,Props,Vals,_uniquesuffix);
|
and set unique id with help of your new property
//string tmp = count == 0 ? "" : Char.ConvertFromUtf32(96 + count); ErrorMessage = errorMessage;
// ValidationType = "requiredif"+tmp;
ValidationType
= "requiredif"+uniquesuffix
|
3- Last one is the way to show proper
error message.
You don't need to add extra property to keep error messages
var reqIfMultipleValidator = function
(value, element, params) {
var others = params.others.split('!');
var reqVals = params.reqval.split('!');
var msgs = params.errorMsgs.split('!');
var errMsg = "";
var values = null;
if (params.values + "" != "")
values = params.values.split('!')
var retVal = true;
var errorIndex=0;
$.each(others, function (index, val)
{
var myParams = { "others":
val, "reqval": reqVals[index],
"values":
values[index] };
retVal = reqIfValidator(value,
element, myParams);
if (retVal
=== false) {
errorIndex=index;
return
false;
}
});
var errormessages=$(element).data('valrequiredifmultiple').split('!');
if
(retVal === false) {
var
evalStr = "this.settings.messages."
+ $(element).attr("name") +
".requiredifmultiple='" + errormessages[errorIndex] + "'";
eval(evalStr);
}
return
retVal;
};
|
Comments