Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active February 14, 2017 22:55
Show Gist options
  • Save dfkaye/aa728f701939a25cfbcd028bc4336f1b to your computer and use it in GitHub Desktop.
Save dfkaye/aa728f701939a25cfbcd028bc4336f1b to your computer and use it in GitHub Desktop.
CampusSolutions: telephone number validation for Intl and US values
'use strict';
var angular = require('angular');
angular.module('calcentral.directives').directive('ccTelPhoneInputDirective', function() {
/**
* 8,9,18,19 AUG 2016
* Regexp-based attempt at parsing tel number input strings
* Note: should use <input type="tel" ...> to display big number buttons in small mobile devices
* Goals:
* + remove leading `+` char from International matches
* + re-format US matches as either `1 xxx yyy zzzz` if leading 1 is
* present, or as `xxx yyy zzzz`
* What it does:
* - replaces one or more consecutive non-word chars with a single space char
* - trims leading and trailing space chars
* Returns object with:
* + `value` containing formatted phone number if matching
* International or US patterns, or the user-entered value if no match.
* + `valid` boolean property set to true if a pattern was matched, false if
* no pattern is matched.
*/
var reIntlPhone = /^(\+?((?:\d ?){6,14}\d))?\s?$/;
var reUsPhone = /^([\d][\W])?(\d{3})\W?[\W]?(\d{3})[\W]?(\d{4})?\s?$/;
var space = ' ';
var blank = '';
var validPhone = function(phone) {
var value = (phone || blank).replace(/[\W]{1,}/gm, space);
var valid = reUsPhone.test(value) || reIntlPhone.test(value);
return {
valid: valid,
value: value
};
};
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, elm, attr, ctrl) {
var validateAndUpdate = function(options) {
var showMessage = options && options.showMessage;
var value = elm.val();
var phone = validPhone(value);
var valid = phone.valid;
if (valid || (!valid && showMessage)) {
ctrl.$setValidity('invalidPhoneNumber', valid);
var viewValue = valid ? phone.value.trim() : phone.value;
ctrl.$setViewValue(viewValue);
ctrl.$render();
}
};
elm.on('keyup', function() {
// The keyup handler updates the view only when a valid field value
// is detected, in order to hide error messages.
validateAndUpdate({
showMessage: false
});
});
elm.on('blur', function() {
// The blur handler updates the view only when an invalid field value
// is detected, in order to show error messages. We use blur rather than
// change in order to prevent link or button default actions.
validateAndUpdate({
showMessage: true
});
});
}
};
});
/**
* 8,9 AUG 2016
* Regexp-based attempt at parsing tel number input strings
* Note: should use <input type="tel" ... /> to display big number buttons in small mobile devices
* - replaces tabs and multiple space chars with single space char
* - trims leading and trailing space chars
* - removes leading `+` char from International matches
* - re-formats US matches as `xxx/yyy-zzz`
* - returns formatted value if matching International or US patterns
* - returns object with `error` property if no pattern is matched.
*/
var reIntlPhone = /^(\+?((?:\d ?){6,14}\d))?$/
var reUsPhone = /^\W?(\d{3})\W?[\W]?(\d{3})[\W]?(\d{4})?$/
var space = ' '
var blank = ''
var validPhone = function(value) {
var phone = (blank + value).replace(/\s{2,}|\t+/gm, space).trim()
if (reIntlPhone.test(phone)) {
return phone.replace(reIntlPhone, "$2")
}
if (reUsPhone.test(phone)) {
return phone.replace(reUsPhone, "$1/$2-$3")
}
return {
error: 'Please re-format the phone number [' + value + ']',
toString: function() {
return value
}
}
}
/**
* use cases by samsonite
*/
var apply = function(value, $scope) {
console.info('applying ', value, 'to ' + $scope)
return $scope.phone = value
}
var error = function(phone) {
console.error(phone.error)
return phone
}
var test = function(value) {
var phone = validPhone(value)
return phone.error ? error(phone) : apply(phone, {
toString: function() {
return 'scope'
}
})
}
/**
* test cases by tourister
*/
console.log(
test('4444 00 00 ')
// intl: 4444 00 00
// 4444 00 00
)
console.log(
test('000.000(0000')
// us: 000.000(0000
// 000/000-0000
)
console.log(
test("44 20 79 25 09 18666")
// intl: 44 20 79 25 09 18666
// 44 20 79 25 09 18666
)
// be liberal in what you accept,...
console.log(
test("(123) 456 1234")
// us: (123) 456 1234
// 123/456-1234
)
console.log(
test('+1234567890')
// intl: +1234567890
// 1234567890
)
console.log(
test('1234567890')
// intl: 1234567890
// 1234567890
)
console.log(
test('123.4567890')
// us: 123.4567890
// 123/456-7890
)
console.log(
test('456&654$4567')
// us: 456&654$4567
// 456/654-4567
)
console.log(
// contains tab character between 13 and 242
test('13 242 5095 5520')
// intl: 13 242 5095 5520
// 13 242 5095 5520
)
// should fail
console.error(
test('h23-456-7891')
// Please re-format the phone number [h23-456-7891]
// h23-456-7891
)
var reIntlPhone = /^(\+?((?:\d ?){6,14}\d))$/;
var reUsPhone = /^([\d][\W])?(\d{3})\W?[\W]?(\d{3})[\W]?(\d{4})$/;
var space = ' ';
var blank = '';
var validPhone = function(phone) {
var value = (phone || blank).replace(/[\W]{1,}/gm, space).trim();
var valid = value && (reUsPhone.test(value) || reIntlPhone.test(value));
return {
valid: valid,
value: value.trim()
};
};
function test(value) {
console.log(validPhone(value))
}
test('')
test('d')
test('11f23 123 1234')
test(' +12 12 33 1234 ')
test('1 123-123-1234 ')
test('(123) 123-1234 ')
test('123/123-1234 ')
test('1.123.123.1234 ')
test('+011 011 1110 ')
test('+99.--99--993')
test('343 343 34343 ')
test('+33 7 52 86 38 27')
test('+65 81182519')
test('+8227937536')
/*
Object { valid="", value=""}
Object { valid=false, value="d"}
Object { valid=false, value="11f23 123 1234"}
Object { valid=true, value="12 12 33 1234"}
Object { valid=true, value="1 123 123 1234"}
Object { valid=true, value="123 123 1234"}
Object { valid=true, value="123 123 1234"}
Object { valid=true, value="1 123 123 1234"}
Object { valid=true, value="011 011 1110"}
Object { valid=true, value="99 99 993"}
Object { valid=true, value="343 343 34343"}
Object { valid=true, value="33 7 52 86 38 27"}
Object { valid=true, value="65 81182519"}
Object { valid=true, value="8227937536"}
*/
<!-- fragment-->
<form data-ng-submit="save(currentObject.data)" name="cc_page_widget_profile_work_experience">
<!-- ... -->
<div class="cc-page-widget-profile-margin-top">
<label for="cc-page-widget-profile-work-experience-phone-number">
<strong>Phone Number</strong>
</label>
</div>
<div>
<input type="text" id="cc-page-widget-profile-work-experience-phone-number"
name="cc_profile_work_experience_phone"
data-cc-tel-phone-input-directive data-ng-model="currentObject.data.phone" maxlength="24">
<p class="cc-page-widget-profile-section-input-error-message cc-text-red" data-ng-show="cc_page_widget_profile_work_experience.$error.invalidPhoneNumber">
<i class="cc-icon-red fa fa-exclamation-circle"></i>
The phone number, <span data-ng-bind="cc_page_widget_profile_work_experience.cc_profile_work_experience_phone.$viewValue"></span>, is incomplete or formatted incorrectly.
</p>
</div>
<!-- ... -->
<div class="cc-page-widget-profile-margin-top">
<button class="cc-button cc-button-blue" type="submit" data-ng-disabled="cc_page_widget_profile_work_experience.$invalid || isSaving || currentObject.stateFieldLoading">
<span data-ng-if="!isSaving">Save</span>
<span data-ng-if="isSaving">Updating&hellip;</span>
</button>
<button class="cc-button" data-ng-click="closeEditor()" data-ng-disabled="isSaving">Cancel</button>
</div>
</form>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment