5

Here is a sample angular directive to prevent typing non-numeric keys (StackOverflow answer). I want to write something like this fiddle to use the is-number directive in several inputs. Please consider that since I have various different directives in my inputs, I cannot use the same template as suggested in the update of mentioned answer above.

var $scope;
var app = angular.module('myapp', []);

app.controller('Ctrl', function($scope) {
    $scope.myNnumber1 = 1;
    $scope.myNnumber2 = 1;
});

app.directive('isNumber', function () {
    return {
        require: 'ngModel',
        link: function (scope, element) {   
            scope.$watch(element.ngModel, function(newValue,oldValue) {
                newValue = String(newValue);
                newValue = newValue.replace('۰', '0').replace('۱', '1').replace('۲', '2').replace('۳', '3').replace('۴', '4').replace('۵', '5').replace('۶', '6').replace('۷', '7').replace('۸', '8').replace('۹', '9');
                var arr = String(newValue).split("");
                if (arr.length === 0) return;
                if (arr.length === 1 && (arr[0] == '-' || arr[0] === '.' )) return;
                if (arr.length === 2 && newValue === '-.') return;
                if (isNaN(newValue)) {
                    element.ngModel = oldValue;
                }
            });
        }
   };

Update: Please consider that I need to do some processes to convert non English numbers and so on. I created a new fiddle here based on the the Angular_10's answer. Now, every thing is fine except the cursor position while typing Persian numbers. When I type A Persian Number, it is replaced with English equivalent number, but the cursor suddenly jumps to the end.

2
  • Please elaborate your question in detail what you want to achieve and what you have tried. Commented Dec 26, 2017 at 6:10
  • @Angular_10 The js and html code that I have written using a directive to prevent entering non numeric keys does not work. Please check the fiddle and let me know how can I fix it. Commented Dec 26, 2017 at 8:20

2 Answers 2

6
+50

OK ! Looking at your requirement I've took liberty and wrote more customised directive.

Here is the fiddle for the same

Problem

The example from which you referred and made changes to the given directive is causing the issue.

  1. Your $scope variable names are wrong in HTML/JS ($scope.myNnumber1 = 1; $scope.myNnumber2 = 1; in JS and in HTML it was ng-model="myNumber1")
  2. You are accessing element ng-model and trying to modify it through directive which is bad practice and also the root cause for directive to not to work.As you are not changing the ng-model value but in turn modifying HTML element value which angular will not recognise.
  3. More over using $watch in directive is not always preferable for performance sake.

Solution

app.directive('isNumber', function() {
    return {
        require: 'ngModel',
        restrict: 'A',
        link: function(scope, element, attr, ctrl) {
            function inputValue(val) {
                if (val) {
                    var numeric = val.replace(/[^- 0-9]/g, '');

                    if (numeric !== val) {
                        ctrl.$setViewValue(numeric );
                        ctrl.$render();
                    }
                    return numeric;
                }
                return undefined;
            }
            ctrl.$parsers.push(inputValue);
        }
    };

});

When controller communication is required from directive we can pass Controller as 4 param in the link function.From that Ctrl param we can modify/view things from controller scope.

Using some basic regex expression to find out what is the entered input and set it in the controller scope object view value.

ctrl.$setViewValue(numeric); //to set the value in the respective ngModdel
ctrl.$render(); //to display the changed value

More about $setViewValue

Sign up to request clarification or add additional context in comments.

3 Comments

Please check the update and consider that I need to do some processes to convert non English numbers. How can I replace these non English numbers to English ones? (I also have updated my original fiddle.)
What are non English numbers ? If you are referring to user entering some non English keyboard values then you must consider using some external libraries to do such kind of conversion to normal numbers
You could capture the cursor position before the $render, and then set it back afterward. var cursor_pos = element.prop('selectionStart') - 1; ctrl.$setViewValue(numeric); ctrl.$render(); element[0].setSelectionRange(cursor_pos, cursor_pos);
0

I finally used the below directive. This directive converts persian number and do not let no numbers to be typed in the text box. Special thanks to Angular_10. I awarded 50 bounties to him for his help.

app.directive('fixPersianAndNoNumberInput', function ($filter) {
    return {
        require: 'ngModel',
        restrict: 'EA',
        link: function (scope, element, attr, controller) {
            function inputValue(val) {
                if (val) {
                    let numeric = parseInt(String(val).replace('۰', '0').replace('۱', '1').replace('۲', '2').replace('۳', '3').replace('۴', '4').replace('۵', '5').replace('۶', '6').replace('۷', '7').replace('۸', '8').replace('۹', '9').replace(' ', '000').replace(/[^- 0-9]/g, ''));
                    if (numeric !== val) {
                        controller.$setViewValue(numeric);
                        controller.$render();
                        let value;
                        let updateOn, debounce;
                        if (controller.$options) {
                            if (controller.$options.getOption) {
                                updateOn = controller.$options.getOption('updateOn');
                                debounce = controller.$options.getOption('debounce');
                            } else {
                                updateOn = controller.$options.updateOn;
                                debounce = controller.$options.debounce;
                            }
                        }
                        if (updateOn === 'blur' || debounce) {
                            value = controller.$viewValue;
                            for (let i = controller.$parsers.length - 1; i >= 0; i--) {
                                value = controller.$parsers[i](value);
                            }
                        } else {
                            value = controller.$$rawModelValue;
                        }
                        for (let j = controller.$formatters.length - 1; j >= 0; j--) {
                            value = controller.$formatters[j](value);
                        }
                        controller.$viewValue = value;
                        controller.$render();
                    }
                    return numeric;
                }
                return undefined;
            }

            controller.$parsers.push(inputValue);
            controller.$formatters.push((value) => {
                if ([undefined, null, ''].indexOf(value) === -1) {
                   return $filter('currency')(value, '', 0);
                }
                return value;
            });
        }
    };
});

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.