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.

Community
  • 1
  • 1
EnigmaRM
  • 7,523
  • 11
  • 46
  • 72
  • is the purpose of `math()` just to make sure that the `compute1` and `compute2` values in the scope are always numeric? – John Ledbetter Nov 25 '13 at 21:11
  • 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. – EnigmaRM Nov 25 '13 at 21:42

5 Answers5

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.

angabriel
  • 4,979
  • 2
  • 35
  • 37
  • 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. – EnigmaRM Nov 25 '13 at 21:40
  • 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. – angabriel Nov 25 '13 at 21:44
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

Community
  • 1
  • 1
Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • 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 `parseInt`s in place. – EnigmaRM Nov 25 '13 at 22:00
  • @downvoder please describe – Maxim Shoustin Sep 12 '14 at 05:29
  • 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" function – O'Mutt Oct 16 '14 at 13:57
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.

Community
  • 1
  • 1
codebreach
  • 2,155
  • 17
  • 30
  • 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. – EnigmaRM Nov 25 '13 at 21:44
  • Well in that case, you probably want a custom directive that saves resets the caret position. There is a great example of this here https://github.com/angular/angular.js/issues/1679 – codebreach Nov 25 '13 at 21:46
  • I had tried implementing my own directive that does this - but I had failed. I'll have to save this for future use. Thanks! – EnigmaRM Nov 25 '13 at 22:02
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}"
Ben
  • 953
  • 12
  • 27
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);
                });
            }
        };
    })
jgerstle
  • 1,674
  • 5
  • 25
  • 35
  • Would you add a JSFiddle so others can play with it when looking for an answer? thanks – EnigmaRM Apr 06 '16 at 15:45
  • Here is a plunker based on this question: http://stackoverflow.com/questions/24701205/how-can-you-limit-the-value-from-input-using-angularjs which uses this code in the watch instead of the change but the result is similar: http://plnkr.co/edit/7glDI3Mh3HtmgVAR2bFz?p=preview – jgerstle May 29 '16 at 08:23