Preface
I know literally nothing about AngularJS. I have never used it before. This is my first time I see it. I don’t know what it does. All it appears to do is add a massive layer of abstraction to obfuscate the code and justify increased IT spending on “insiders” who can see through this obfuscation. Your task would be much more simple without AngularJS. But, as I said, I don’t know what AngularJS is nor what it does, so I could be wrong.
The basic solution
The reason for the reported behaviour is that your script overwrites the <input>
value every time the user types anything. The usual and basic solution is to store the caret position before the act of overwriting (element.val(...)
):
var caretPosition = element[0].selectionStart;
...and restore it immediately after:
element[0].focus(); element[0].setSelectionRange(caretPosition, caretPosition);
Your script does contain a stub of a caret handling code already but it does literally nothing, it merely repeats its own unintended behaviour by intentionally moving the caret to the end of the string. This pointless act appears to be a hint to anyone who reuses the code to write their own caret handling code. Replace that stub with my code.
Live example
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {$scope.name = 'Phone Number';});
app.directive('abcXyz', function($filter) {
var mobileFilter, mobileReverse;
mobileFilter = $filter('mobileFilter');
mobileReverse = $filter('mobileReverse');
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
var formatter, parser;
parser = function(value) {
var formatted;
formatted = mobileReverse(value);
/* solution */ var caretPosition = element[0].selectionStart;
/* solution */ if (caretPosition === 4) caretPosition += 3;
/* solution */ if (caretPosition === 10) ++caretPosition;
element.val(mobileFilter(formatted));
/* solution */ element[0].selectionStart = element[0].selectionEnd = caretPosition;
return formatted;
};
modelCtrl.$formatters.push(formatter);
return modelCtrl.$parsers.unshift(parser);
}
};
});
app.filter('mobileFilter', function() {
return function(value) {
var len, val;
if (!value) return;
val = value.toString().replace(/\D/g, "");
len = val.length;
if (len < 4) return val;
else if (3 < len && len < 7)
return "(" + (val.substr(0, 3)) + ") " + (val.substr(3));
else if (len > 6)
return "(" + (val.substr(0, 3)) + ") " + (val.substr(3, 3)) +
"-" + (val.substr(6, 4));
return value;
}
});
app.filter("mobileReverse", () => value => !!value && value.replace(/\D/g, "")
.substr(0, 10));
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js">
</script>
<html ng-app="plunker">
<body ng-controller="MainCtrl">
<p>{{name}}!</p>
<input type="tel" abc-xyz ng-model="formData.Phone" name="worktype"
maxlength="14" required="required" ng-pattern="(/[0-9-()]*[1-9][0-9-()]*/);"
autoComplete="off">
</body>
</html>
A complete solution
The basic solution is an improvement but it’s still not perfect. It fails with backspace. User can sometimes add a letter or delete mandatory dash or bracket. It seems that you need a complex caret handler that has awareness of backspace, the filter and not one but two last caret positions. I have already included simple filter awareness in the basic example:
if (caretPosition === 4) caretPosition += 3;
if (caretPosition === 10) ++caretPosition;
but it is insufficient. At this point, I’m unable to deliver a complete caret handler to you but at least let me show you how to include backspace awareness in your AngularJS code:
- Add attribute
ng-keydown="registerBackspace($event)"
to HTML tag <input type="tel">
.
- Replace your
app.controller()
call with this call:
app.controller('MainCtrl', function($scope) {
$scope.name = 'Phone Number';
$scope.registerBackspace = event => window.phoneBackspace = event.keyCode === 8;
});
- Now you have a global variable
window.phoneBackspace
which indicates whether the last key pressed in the phone input field was backspace (true
or false
).
I dislike global variables but I know nothing about AngularJS to figure out how to pass such information using its own functions (nor whether it allows doing so at all).
Final words
Upvote if you think that moderators are idiots.
Literature