Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent entering any additional characters

Tags:

I'm using this Angular Directive to format phone numbers in an input to (999) 999-9999. This works great until a user makes a mistake and modifies the entered phone number.

You can replicate this issue by running the code below and doing the following:

• Enter the phone number (555) 123-4567
• Place your cursor after the 4 character and delete it.
• Type in 0 twice.

You can see that the 0 is added twice and the 7 character is dropped.

Another issue is if a user attempts to delete and change the 1 character. Their cursor is pushed to the very end of the input.

I'm sure this is due to an issue with the phonenumber filter, but I'm not sure how to approach this.

function MyCntl($scope) {      $scope.myModel = {};      $scope.myPrompt = "Input your phonenumber here!";  }    var phonenumberModule = angular.module('phonenumberModule', [])     	.directive('phonenumberDirective', ['$filter', function($filter) {  		/*  		Intended use:  			<phonenumber-directive placeholder='prompt' model='someModel.phonenumber'></phonenumber-directive>  		Where:  			someModel.phonenumber: {String} value which to bind only the numeric characters [0-9] entered  				ie, if user enters 617-2223333, value of 6172223333 will be bound to model  			prompt: {String} text to keep in placeholder when no numeric input entered  		*/     		function link(scope, element, attributes) {     			// scope.inputValue is the value of input element used in template  			scope.inputValue = scope.phonenumberModel;     			scope.$watch('inputValue', function(value, oldValue) {  				  				value = String(value);  				var number = value.replace(/[^0-9]+/g, '');  				scope.phonenumberModel = number;  				scope.inputValue = $filter('phonenumber')(number);  			});  		}  		  		return {  			link: link,  			restrict: 'E',  			scope: {  				phonenumberPlaceholder: '=placeholder',  				phonenumberModel: '=model',  			},  			//templateUrl: '/static/phonenumberModule/template.html',  			              template: '<input name="phonenumber" ng-model="inputValue" type="tel" class="phonenumber" placeholder="{{phonenumberPlaceholder}}" title="Phonenumber (Format: (999) 9999-9999)">',  		};  	}])     	.filter('phonenumber', function() {  	    /*   	    Format phonenumber as: c (xxx) xxx-xxxx  	    	or as close as possible if phonenumber length is not 10  	    	if c is not '1' (country code not USA), does not use country code  	    */  	      	    return function (number) {  		    /*   		    @param {Number | String} number - Number that will be formatted as telephone number  		    Returns formatted number: (###) ###-####  		    	if number.length < 4: ###  		    	else if number.length < 7: (###) ###     		    Does not handle country codes that are not '1' (USA)  		    */  	        if (!number) { return ''; }     	        number = String(number);     	        // Will return formattedNumber.   	        // If phonenumber isn't longer than an area code, just show number  	        var formattedNumber = number;     			// if the first character is '1', strip it out and add it back  			var c = (number[0] == '1') ? '1 ' : '';  			number = number[0] == '1' ? number.slice(1) : number;     			// # (###) ###-#### as c (area) front-end  			var area = number.substring(0,3);  			var front = number.substring(3, 6);  			var end = number.substring(6, 10);     			if (front) {  				formattedNumber = (c + "(" + area + ") " + front);	  			}  			if (end) {  				formattedNumber += ("-" + end);  			}  			return formattedNumber;  	    };  	});
.phonenumber {      min-width: 200px;  }
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>    <div ng-app="phonenumberModule" ng-controller="MyCntl">      <p>phonenumber value: {{ myModel.phonenumber }}</p>      <p>formatted phonenumber: {{ myModel.phonenumber | phonenumber }}</p>      <form name="phoneForm">          <phonenumber-directive placeholder="myPrompt" model='myModel.phonenumber'></phonenumber-directive>          <div ng-show="phoneForm.phonenumber.$error.minlength">            <p>Enter a valid phone number</p>          </div>      </form>  </div>
like image 905
floatleft Avatar asked Jul 16 '15 17:07

floatleft


People also ask

How do I turn off input field letters?

The typical way to prevent input of certain characters is to listen to the keydown event, check the character that is being input, then use event. preventDefault() to stop the input from occurring.


2 Answers

Modifying a user's input as they type can be distracting and always leads to problems like those you describe.

In general, it is easier to implement and less frustrating to the user to modify the UI instead. A common solution for the case of phone numbers is to put three input fields side-by-side, with the formatting characters displayed in-between.

Using Angular, a single directive can be created to wrap this multi-part input mechanism and expose a single concatenated result to the rest of the app.

like image 136
starchild Avatar answered Sep 23 '22 19:09

starchild


I think you should use an editable field (like input of type text) to enter the number and a read-only field (like a label) to show it formatted, because the formatted value is just a display concern, so it should not be editable.

So I modified your snippet to do that.

function MyCntl($scope) {      $scope.myModel = {};      $scope.myPrompt = "Input your phonenumber here!";  }    var phonenumberModule = angular.module('phonenumberModule', [])     	.directive('phonenumberDirective', ['$filter', function($filter) {  		/*  		Intended use:  			<phonenumber-directive placeholder='prompt' model='someModel.phonenumber'></phonenumber-directive>  		Where:  			someModel.phonenumber: {String} value which to bind only the numeric characters [0-9] entered  				ie, if user enters 617-2223333, value of 6172223333 will be bound to model  			prompt: {String} text to keep in placeholder when no numeric input entered  		*/     		function link(scope, element, attributes) {     			// scope.inputValue is the value of input element used in template  			scope.inputValue = scope.phonenumberModel;     			scope.$watch('inputValue', function(value, oldValue) {  				  				value = String(value);                  oldValue = String(oldValue);                  // get rid of input non digits chars  				var number = value.replace(/[^0-9]+/g, '');                  var oldNumber = oldValue.replace(/[^0-9]+/g, '');  				                  var filteredNumber = $filter('phonenumber')(number);                  // get rid of filter non digits chars                  scope.phonenumberModel = filteredNumber.replace(/[^0-9]+/g, '');                  inputValue = scope.phonenumberModel;                  var filteredOldNumber = $filter('phonenumber')(oldNumber);                  if(filteredNumber.length === filteredOldNumber.length) {                      scope.maxlength = filteredNumber.length;                  }                  else {                      scope.maxlength = Math.max(number.length, 11);                  }                      			});  		}  		  		return {  			link: link,  			restrict: 'E',  			scope: {  				phonenumberPlaceholder: '=placeholder',  				phonenumberModel: '=model',  			},  			//templateUrl: '/static/phonenumberModule/template.html',  			              template: '<input name="phonenumber" ng-model="inputValue" type="tel" maxlength="{{maxlength || 11}}" class="phonenumber" placeholder="{{phonenumberPlaceholder}}" title="Phonenumber (Format: (999) 9999-9999)"> <label>Formatted:{{inputValue | phonenumber}}</label>',  		};  	}])     	.filter('phonenumber', function() {  	    /*   	    Format phonenumber as: c (xxx) xxx-xxxx  	    	or as close as possible if phonenumber length is not 10  	    	if c is not '1' (country code not USA), does not use country code  	    */  	      	    return function (number) {  		    /*   		    @param {Number | String} number - Number that will be formatted as telephone number  		    Returns formatted number: (###) ###-####  		    	if number.length < 4: ###  		    	else if number.length < 7: (###) ###     		    Does not handle country codes that are not '1' (USA)  		    */  	        if (!number) { return ''; }     	        number = String(number);     	        // Will return formattedNumber.   	        // If phonenumber isn't longer than an area code, just show number  	        var formattedNumber = number;     			// if the first character is '1', strip it out and add it back  			var c = (number[0] == '1') ? '1 ' : '';  			number = number[0] == '1' ? number.slice(1) : number;     			// # (###) ###-#### as c (area) front-end  			var area = number.substring(0,3);  			var front = number.substring(3, 6);  			var end = number.substring(6, 10);     			if (front) {  				formattedNumber = (c + "(" + area + ") " + front);	  			}  			if (end) {  				formattedNumber += ("-" + end);  			}  			return formattedNumber;  	    };  	});
.phonenumber {      min-width: 200px;  }
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>    <div ng-app="phonenumberModule" ng-controller="MyCntl">      <p>phonenumber value: {{ myModel.phonenumber }}</p>      <p>formatted phonenumber: {{ myModel.phonenumber | phonenumber }}</p>      <form name="phoneForm">          <phonenumber-directive placeholder="myPrompt" model='myModel.phonenumber'></phonenumber-directive>          <div ng-show="phoneForm.phonenumber.$error.minlength">            <p>Enter a valid phone number</p>          </div>      </form>  </div>
like image 31
Xartok Avatar answered Sep 21 '22 19:09

Xartok