0

I'm looking to use regex to do some form manipulation based on the user's credit card input (note: this is only front-end validation for the sake of UX, another service and API handles actual credit card validation).

I'd like to create a $watch statement or some equivalent to match a user's input in the credit card field against several regex statements that are ascribed to different card types.

So, my question in a nutshell is: what's the best pattern to implement that can match against multiple regent statements without degrading performance with too many watchers? My first thought was to write multiple if or switch statements, but that seems like a problem for extensibility and a complicated bit of logic isn't often the best solution.

Thanks everyone!

Here's what I have so far:

    var defaultFormat = /(\d{1,4})/g;

    $scope.cards = [{
        type: 'maestro',
        pattern: /^(5018|5020|5038|6304|6759|676[1-3])/,
        format: defaultFormat,
        length: [12, 13, 14, 15, 16, 17, 18, 19],
        cvcLength: [3],
        luhn: true
    }, {
        type: 'dinersclub',
        pattern: /^(36|38|30[0-5])/,
        format: defaultFormat,
        length: [14],
        cvcLength: [3],
        luhn: true
    }, {
        type: 'laser',
        pattern: /^(6706|6771|6709)/,
        format: defaultFormat,
        length: [16, 17, 18, 19],
        cvcLength: [3],
        luhn: true
    }, {
        type: 'jcb',
        pattern: /^35/,
        format: defaultFormat,
        length: [16],
        cvcLength: [3],
        luhn: true
    }, {
        type: 'unionpay',
        pattern: /^62/,
        format: defaultFormat,
        length: [16, 17, 18, 19],
        cvcLength: [3],
        luhn: false
    }, {
        type: 'discover',
        pattern: /^(6011|65|64[4-9]|622)/,
        format: defaultFormat,
        length: [16],
        cvcLength: [3],
        luhn: true
    }, {
        type: 'mastercard',
        pattern: /^5[1-5]/,
        format: defaultFormat,
        length: [16],
        cvcLength: [3],
        luhn: true
    }, {
        type: 'amex',
        pattern: /^3[47]/,
        format: /(\d{1,4})(\d{1,6})?(\d{1,5})?/,
        length: [15],
        cvcLength: [3, 4],
        luhn: true
    }, {
        type: 'visa',
        pattern: /^4/,
        format: defaultFormat,
        length: [13, 16],
        cvcLength: [3],
        luhn: true
    }];

I also have a luhn algorithm that I can use to check the credit cards.

luhnCheck = function(num) {
var digit, digits, odd, sum, _i, _len;
odd = true;
sum = 0;
digits = (num + '').split('').reverse();
for (_i = 0, _len = digits.length; _i < _len; _i++) {
  digit = digits[_i];
  digit = parseInt(digit, 10);
  if ((odd = !odd)) {
    digit *= 2;
  }
  if (digit > 9) {
    digit -= 9;
  }
  sum += digit;
}
return sum % 10 === 0;
};
2
  • Why not just use one $watch and get the type of your card when the model changes? Commented Jun 9, 2014 at 23:27
  • That could work; I'm a bit new to JS/Angular, so I'm wondering how you'd implement executing the regex statements? Commented Jun 9, 2014 at 23:29

2 Answers 2

1

Plunker

You're right, you'd want to watch the value of a model in order to change the "type" stored in a variable somewhere.

Here's an example of a relevant watch statement:

    $scope.$watch( 'model', function()
    {
      var found = false;

      angular.forEach( $scope.cards, function( item, index )
      {
        if ( $scope.model.match( item.pattern ) )
        {
          $scope.card_type = item.type;
          found = true;
        }
      });

      // You could run your Luhn method here too

      if ( !found ) $scope.card_type = 'none';
    });
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! I appreciate the help and especially the plunker. I can see your solution works and will probably implement a version of it. One question though, if you you don't mind: what role does the HTTP GET request play? Is there a way to do the same action without JSON-ifying anything or making an additional request? Thanks again!
@markimus Whoops good question, that was cruft left behind when I tried to throw the $scope.cards array into a .json file, but I think .json requires quotes around the keys, unlike JS. I've updated the Plunker; totally ignore the $http bit.
Gotcha! I suppose I could have just nixed it and seen what would of happen, but I thought I would ask anyways. Thanks again!
0

This is an untested solution:

On the credit card input form field:

<input type="text" ng-change="matchCCTypes()" ng-model="ccnum">

Controller:

$scope.ccnum = "";
$scope.ccType = null;

$scope.matchCCTypes() = function() {
  $scope.ccType = null;
  angular.forEach($scope.cards, function(cardType) {
    // pseudo code:
    // if ($scope.ccnum matches current pattern) {
    //   $scope.ccType = cardType.type
    // }
  });
};

This way, you can avoid setting up a watcher. The function will run only when the ccnum field changes.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.