11

I need to change the caret position of an input, where a given number of digits is added (Example).

app.controller('MainCtrl', function($scope, $element, $timeout, $filter) {
  //$scope.val = '12';
  $scope.$watch('val', function(newValue, oldValue) {
    if (!isNaN(newValue)) {
      if (newValue.length > 3) {
        //Set Caret Position  
      }
    }
  });
});

Is it possible to do something like this example?

I need for example :

Input: 1234.

so the caret position will be 2.

New digit: 9

final: 12934

Thanks in advance.

5 Answers 5

27

I think that such kind of things look better in directives. For example:

app.directive('caret', function() {

    function setCaretPosition(elem, caretPos) {
        if (elem !== null) {
            if (elem.createTextRange) {
                var range = elem.createTextRange();
                range.move('character', caretPos);
                range.select();
            } else {
                if (elem.setSelectionRange) {
                    elem.focus();
                    elem.setSelectionRange(caretPos, caretPos);
                } else
                    elem.focus();
            }
        }
    }

    return {
        scope: {value: '=ngModel'},
        link: function(scope, element, attrs) {
            var caret = Number(attrs.caret);
            scope.$watch('value', function(newValue, oldValue) {
                if (newValue && newValue != oldValue && !isNaN(newValue) && newValue.length > (caret + 1)) {
                    setCaretPosition(element[0], caret);
                }
            });
        }
    };
});

Usage:

<input ng-model='val' caret="2" />

I used setCaretPosition function for cross browser cursor positioning from this answer.

Demo: http://plnkr.co/edit/5RSgzvyd8YOTaXPsYr8A?p=preview

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

1 Comment

It's like I wanted. How could reuse the same directive in other inputs? (plnkr.co/edit/6UARwnie2VOpHhwY5QUB?p=preview). When should back the cursor to the next input is redirected.
1

I think that the best approach for this is to make a reusable directive as we are dealing with DOM manipulation.

Link to the demo: http://plnkr.co/edit/qlGi64VO1AOrNpxoKA68?p=preview

var app = angular.module('angularjs-starter', []);

app.controller('MainCtrl', function($scope, $element, $timeout, $filter) {
  $scope.$watch('val', function(newValue, oldValue) {
    if (!isNaN(newValue)) {
      if (newValue.length > 3) {
      // $element.find('input')[0].selectionEnd  = 2;
      }
    }
  });
});

app.directive('setCaret', function() {

  return {
    restrict: 'A',
    link: function(scope,element,attrs) {
      var changed = false;
      element.bind('keypress', function() {
        if(element[0].selectionStart > 3 && !changed) {
          changed = true;
          element[0].selectionEnd = parseInt(attrs.position, 10);
        }
      })

    },
  }

})

You can see in the commented out part in the controller we can have access to this by using $element, but as this is DOM and controllers are not for DOM manipulation we need to make this into a directive.

Comments

1

I also had the same problem.

I thought to solve it creating an appropriate directive. You can find it here. Enjoy it!

Usage

Include directive, declare it by caret-aware attribute

<script src="https://cdn.rawgit.com/leodido/ng-caret-aware/master/caretaware.min.js"></script>
<script type="text/javascript">
  var app = angular.module('myModule', ['leodido.caretAware']);
</script>
...
<div data-ng-app="app">
    <input type="text" name="myname" caret-aware="cursor"/>
</div>

Then on the scope you'll have a variable cursor containing the position of the caret in the input named myname.

Nevertheless, this directive's controller exposes an API

  • getPosition
  • setPosition

For other usage examples see example directory of the above linked github repository.

Comments

0

I believe you could do it by using .setSelectionRange() on your input. I updated your example - see if this is what you wanted: http://plnkr.co/edit/bIJAPPAzkzqLIDUxVlIy?p=preview

Note: setSelectionRange is not supported by IE8 (see https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement.setSelectionRange), so if you need to support IE < 9, you'll need to look for shims.

Comments

0

I jsfiddled a working solution. So basically, you have to create a directive :

app.directive('keypressdetector', function($compile){
    return {
    restrict:'AEC',
    link: function(scope, element, attrs){
        element.bind("keypress", function (event) {
            if(event.which === 13) {
                            var selectionStart = element[0].selectionStart;
              var value = element.val();
              var valueLength = value.length;
              var newValue= '';
              if (selectionStart == valueLength){
                newValue = value;
              } else {
                newValue = value.substring(selectionStart, valueLength);
              }
              var newElement = angular.element('<input type="text" value="' + newValue +'"/>')
              angular.element(document.body).append(newElement);
            }
        });
    }
  };
});

Your controller would be useless in that situation. You can invoke the directive like this (see : keypressdetector) :

<div ng-app="myapp">
<div ng-controller="LoginController">
    <div>Hello {{ user.firstName }}</div>
    <input ng-model="user.firstName" keypressdetector />
    <input type="submit" ng-click="login()" value="Login"/>
    <div ng-repeat="login in logins">{{ login }}</div>
</div>
</div>

See demo : https://jsfiddle.net/Lt7aP/3468/

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.