Input cursor position jumps to end with ng-change()
Asked Answered
E

5

3

I have an instance where I'm replacing the value of ngModel through ngChange. The cursor jumps to the end of the input field after each change (assuming because I'm assigning the result to the same $scope variable.)
I'd like to know how I can prevent this behavior?

  $scope.compute1 = 0;
  $scope.compute2 = 10;

  $scope.math = function() {
    $scope.compute1 = parseInt($scope.compute1);
    $scope.compute2 = parseInt($scope.compute2);

    $scope.compute1 = parseInt($scope.compute1);
  };

fiddle

Example of problem: if a user types in 1000. It's fine. But then if they want to go back and change the number to 156000 by just adding the 5 and 6, the 6 would actually be appended to the end: 15006.

Eclipse answered 25/11, 2013 at 21:1 Comment(2)
is the purpose of math() just to make sure that the compute1 and compute2 values in the scope are always numeric?Perichondrium
No. Math() is a crude representation that I'm doing a bunch of math stuff there. I should have explained that. Obviously there isn't anything happening in this function.Eclipse
E
2

The cursor goes to the end because we modify data by using parseInt.

I suggest you to store the caret position before and then when you're done doing things, set it back.

This example might help you: Link

Edith answered 25/11, 2013 at 21:22 Comment(3)
You were correct. It was baffling me why even on a input where I wasn't reassigning the model value - the problem was still occurring. I had some unnecessary parseInts in place.Eclipse
@downvoder please describeEdith
This is what i am looking for but i'm having some issues impl the get/set cursor on the same input, any links to something that is dealing with the same user input and changing the values, the link provided is more of a "move to" functionFridlund
C
2

Two suggestions:

1 Why not just use number input.

<div ng-app='myApp'>
    <div ng-controller="myCtrl">
    <input id="compute1" ng-model="compute.c1" ng-change="math()" type="number"/>
    <input id="compute2" ng-model="compute.c2" ng-change="math()" type="number"/>
  </div>
</div>

2 Two-Way databinding should always be used with the "dot" notation:

$scope.compute = {c1: 0, c2: 10};

$scope.math = function() {
  $scope.compute.c1 = parseInt($scope.compute.c1);
  $scope.compute.c2 = parseInt($scope.compute.c2);
};

and update your html accordingly to have ng-model="compute.c1" etc.

Climactic answered 25/11, 2013 at 21:15 Comment(2)
I'm aware of dot notation specification. But this is just a crude example of what is happening. In my actual code I'm using dot - but that doesn't affect the end result.Eclipse
i was about to delete the answer after 1minute as i realized this doesnt answer your question. Sorry about that, but in my opinion this combination of ng-change + ng-model is a design problem. Especially from usability point of view the user is always irritated, when he/she types text (or numbers) and the input magically changes under his/her fingers.Climactic
E
2

The cursor goes to the end because we modify data by using parseInt.

I suggest you to store the caret position before and then when you're done doing things, set it back.

This example might help you: Link

Edith answered 25/11, 2013 at 21:22 Comment(3)
You were correct. It was baffling me why even on a input where I wasn't reassigning the model value - the problem was still occurring. I had some unnecessary parseInts in place.Eclipse
@downvoder please describeEdith
This is what i am looking for but i'm having some issues impl the get/set cursor on the same input, any links to something that is dealing with the same user input and changing the values, the link provided is more of a "move to" functionFridlund
L
1

Depending on what math() does, you could make the computation happen on blur instead of change. This way the conversion will only happen when the user tabs (or clicks) out of the input.

See Angularjs: input[text] ngChange fires while the value is changing for an example of this.

Lelialelith answered 25/11, 2013 at 21:42 Comment(3)
This was my first idea as well. But the higher-ups really liked the idea of 'instant' results. I should've just used blur from the beginning & not even let them know about ng-change. haha. +1 for a valid solution. But I'll be needing something a bit different.Eclipse
Well in that case, you probably want a custom directive that saves resets the caret position. There is a great example of this here github.com/angular/angular.js/issues/1679Lelialelith
I had tried implementing my own directive that does this - but I had failed. I'll have to save this for future use. Thanks!Eclipse
M
1

Very quick and simple fix to delay the ng-model-options. I had the same issue this worked for me: on your input ->

ng-model-options="{debounce: 750}"
Michey answered 31/7, 2018 at 11:25 Comment(0)
U
0

I just wrote this directive which basically allows you to format the text and keep cursor position it's not flawless but works pretty well. Just make sure to return the value in the format function instead of actually changing the value and use it the same as you would the regular ng-change:

.directive('ngChangeFormat', function() {
        return {
            restrict: 'A',
            require: '?ngModel',
            link: function(scope, element, attr, controller) {
                controller.$viewChangeListeners.push(function() {
                    var el = element[0];
                    var start = el.selectionStart;
                    var end = el.selectionEnd;
                    var originalValue = controller.$viewValue;
                    var formattedValue = scope.$eval(attr.ngChangeFormat);
                    controller.$setViewValue(formattedValue);
                    controller.$render();
                    if(start === originalValue.length)
                        el.setSelectionRange(formattedValue.length, formattedValue.length);
                    else
                        el.setSelectionRange(start, end);
                });
            }
        };
    })
Urnfield answered 5/4, 2016 at 8:46 Comment(2)
Would you add a JSFiddle so others can play with it when looking for an answer? thanksEclipse
Here is a plunker based on this question: #24701705 which uses this code in the watch instead of the change but the result is similar: plnkr.co/edit/7glDI3Mh3HtmgVAR2bFz?p=previewUrnfield

© 2022 - 2024 — McMap. All rights reserved.