2
Vote

RequiredIf using Regex against number field never validates

description

I have a state field that is only required if the user select USA or Canada from a dropdown. I used RequiredIf with the RegExMatch operator. This works perfectly with server side validation, but the unobtrusive validation never shows that field as invalid.

[RequiredIf("Country", Operator.RegExMatch, "(1033|4105)", ErrorMessage = "{0} is required")]
public string State { get; set; }

[Required(ErrorMessage = "{0} is required")]
public int Country { get; set; }

Looking at the source of mvcfoolproof.unobtrusive.js, the issue is because it is parsing value2 ("(1033|4105)") to a number because it sees that value1 ("1033") is a number. This makes the RegEx never fail because value2 is NaN.

Here is how I changed that block of code to make it work. Essentially I'm keeping the original values around even after being parsed, because you should never parse the regex pattern.

===== Code Fix =====
var originalValue1 = value1;
var originalValue2 = value2;
if (isDate(value1)) {
    value1 = Date.parse(value1);
    value2 = Date.parse(value2);
}
else if (isBool(value1)) {
    if (value1 == "false") value1 = false;
    if (value2 == "false") value2 = false;
    value1 = !!value1;
    value2 = !!value2;
}
else if (isNumeric(value1)) {
    value1 = parseFloat(value1);
    value2 = parseFloat(value2);
}

switch (operator) {
    case "EqualTo": if (value1 == value2) return true; break;
    case "NotEqualTo": if (value1 != value2) return true; break;
    case "GreaterThan": if (value1 > value2) return true; break;
    case "LessThan": if (value1 < value2) return true; break;
    case "GreaterThanOrEqualTo": if (value1 >= value2) return true; break;
    case "LessThanOrEqualTo": if (value1 <= value2) return true; break;
    case "RegExMatch": return (new RegExp(originalValue2)).test(value1); break;
    case "NotRegExMatch": return !(new RegExp(value2)).test(value1); break;
}

comments

jwynveen wrote Nov 26, 2012 at 9:56 PM

The other option would be to parse value1 and value2 separately since they very well could be different types. Something like this:
if (isDate(value1)) {
    value1 = Date.parse(value1);
}
else if (isBool(value1)) {
    if (value1 == "false") value1 = false;
    value1 = !!value1;
}
else if (isNumeric(value1)) {
    value1 = parseFloat(value1);
}
if (isDate(value2)) {
    value2 = Date.parse(value2);
}
else if (isBool(value2)) {
    if (value2 == "false") value2 = false;
    value2 = !!value2;
}
else if (isNumeric(value2)) {
    value2 = parseFloat(value2);
}
==end code==
That could obviously be cleaned up into a shared function that parses each value, but I think you get the point.