I am trying to Validate Postal code and Phone Number in Text box using Angularjs. But it\'s not working
Edit: This will only work for regular expressions with starting ^
and ending $
metacharacters.
Like many of my StackOverflow answers, I'm super late to this party. This question got me on the right track, but none of the answers given were good enough to make a final solution.
Here's something I wrote recently after reading this thread which didn't fully answer my question, which made my code reviewer exclaim, "WTF IS THIS?!" Followed by him hitting the MERGE button.
My problem was I had a form input which I wanted to accept and validate against latitude or longitude regular expressions. I wanted to match against DMS, DDM, and DD style inputs. I could have written one giant expression, but I also needed to test the input at the component level to determine which type the user input and convert it to DD. The expressions for lat/long would make this answer even more complicated, so I am using what I think the original author intended.
If you're interested in lat/long expressions, here's a gist: https://gist.github.com/pjobson/8f44ea79d1852900457bc257a4c9fcd5
Here's kind of a rewrite of my code for this exercise, I abstracted it from angular to make it more accessible as it could be used for other purposes.
It looks like you're accepting: North America Zip Code OR UK Zip Code OR something which starts with 0-9.
Fleshing out the regex a bit, I haven't tested these myself very well, so your mileage may vary. This is what the need seemed to look like to me, it is just sample code, so it doesn't really matter that much 3+ years later.
N.America Zip or Zip+4 (from sample above)
^(\d{5}(-\d{4})?$
UK Zip Code (from wikipedia)
^([A-Za-z][A-Ha-hJ-Yj-y]?[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}|[Gg][Ii][Rr] ?0[Aa]{2})$
N.America Phone Number (from RegExLib.com)
^[2-9]\d{2}-\d{3}-\d{4}$
UK Phone Number (from RegExLib.com)
^(\s*\(?0\d{4}\)?\s*\d{6}\s*)|(\s*\(?0\d{3}\)?\s*\d{3}\s*\d{4}\s*)$
In an object...
const expressions = {
naZip: /^\d{5}(-\d{4})?$/,
ukZip: /^([A-Za-z][A-Ha-hJ-Yj-y]?[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}|[Gg][Ii][Rr] ?0[Aa]{2})$/,
naPhone: /^[2-9]\d{2}-\d{3}-\d{4}$/,
ukPhone: /^(\s*\(?0\d{4}\)?\s*\d{6}\s*)|(\s*\(?0\d{3}\)?\s*\d{3}\s*\d{4}\s*)$/
};
Now the problem is ng-pattern ONLY accepts one regex, but I have four. I want to keep those as four different things so in my component I can test to figure out which one the user input and do different actions based on whatever it is.
This looks like garbage, but it works really well.
const combinedExpression = new RegExp(`^(${_.map(expressions, exp => `(${exp.toString().replace(/^\/\^(.+?)\$\/$/,'$1')})`).join('|')})$`);
From here of course you can add this to your model and set your ng-pattern to it.
I'll try to explain this in several different ways here...
Here is a break down of what it is doing.
new RegExp(`^(${_.map(expressions, exp => `(${exp.toString().replace(/^\/\^(.+?)\$\//g,'$1')})`).join('|')})$`);
^ ^^^^ ^ ^ ^ ^ ^ ^ ^ ^ ^^
| |||| | | | | | | | | ||
| |||| | | | | | | | | |+- end of the 1st template literal
| |||| | | | | | | | | +- end the expression with $
| |||| | | | | | | | +- join all stringified expressions with a pipe so (a|b|c|d)
| |||| | | | | | | +- end of 2nd template literal
| |||| | | | | | +- replace the like /^(\d{5}(-\d{4})?$/ to (\d{5}(-\d{4})?
| |||| | | | | +- convert the expression to a string
| |||| | | | +- new Expression interpolation
| |||| | | +- 2nd templte literal
| |||| | +- each expression is represented by exp
| |||| +- Using lodash map the expressions
| |||+- Expression interpolation
| ||+- Regex group match, we will be grouping each of the expressions
| |+- Regex starts with ^
| +- Starting a new template literal
+- Establish a new combined expression.
Further clarification...
What I'm doing is taking each of the expressions, converting them to strings, removing the starting end ending characters, joining them back together as an OR Group and then converting the whole thing to one huge combined regular expression.
Further clarification...
If that was too confusing, which I can see how it may be...
/^(\d{5}(-\d{4})?$/
converts to (\d{5}(-\d{4})?
.(a|b|c|d)
where a, b, c, d
are each of your stringified expressions.^
and ends with $
.new RegExp
.Further clarification, in long hand JS.
// Instantiate a new empty array
const strings = [];
// Loop the expressions
for (let key in expressions) {
// Console log to see what we're getting
console.log('original expression', expressions[key]);
// assign the current expression
let stringifiedExp = expressions[key];
// convert the current expression to a string
stringifiedExp = stringifiedExp.toString();
// replace the starting /^ and ending $/ with the contents
stringifiedExp = stringifiedExp.replace(/^\/\^(.+?)\$\/$/,'$1');
// Console log to see what we got.
console.log('stringified expression', stringifiedExp);
// Push the stringified expression to strings.
strings.push(stringifiedExp);
}
// Instantiate a new string which we'll build the regex with
let expString = '^(';
expString += strings.join('|');
expString += ')$';
// Console log to see what we've got.
console.log(expString);
// Make a new Regular Expression out of the string
const combinedExpression = new RegExp(expString);
// Console log what we've got
console.log(combinedExpression);