4

I have written a custom directive for validation of my form fields. When certain criteria are met (i.e. it is dirty and valid), I want to set the focus automatically to the next input element. This is a requirement from my users, such that they can move through the forms most efficiently.

The simplified directive looks like this:

directive('custom', ['$parse', function($parse) {
    return {
        restrict: 'A',
        require: ['ngModel', '^ngController'],
        link: function(scope, element, attrs, ctrls) {
           var model=ctrls[0], form=ctrls[1];

           scope.next = function(){
                return model.$valid
            }

            scope.$watch(scope.next, function(newValue, oldValue){
                if (newValue && model.$dirty){
                    ???
                }
            })

Now my question is: how can I identify - the next input element (which is the next sibling) or possibly via the tabindex - and focus on it without using Jquery?

For me, it is currently not clear, how to get to the next input element from the available "scope" or "element" attributes without Jquery; and JQlite does nothave a "focus" method. Basically, I need a working substitute for ??? in my code.

Any help is highly appreciated. Thanks Juergen

Mick
  • 8,203
  • 10
  • 44
  • 66
schacki
  • 9,401
  • 5
  • 29
  • 32
  • Maybe, the following may be helpful: http://stackoverflow.com/questions/18086865/angularjs-move-focus-to-next-control-on-enter – Eduardo Cuomo Apr 30 '16 at 22:03

4 Answers4

5

You can use [0] to get the underlying input element (which has a focus() function) from the angular/jqLite object (which doesn't).

app.directive('custom', ['$parse', function($parse) {
    return {
        restrict: 'A',
        require: ['ngModel'],
        link: function(scope, element, attrs, ctrls) {
           var model=ctrls[0], form=ctrls[1];

           scope.next = function(){
                return model.$valid;
            }

            scope.$watch(scope.next, function(newValue, oldValue){
                if (newValue && model.$dirty)
                {
                    var nextinput = element.next('input');
                    if (nextinput.length === 1)
                    {
                        nextinput[0].focus();
                    }
                }
            })
        }
    }
}])

http://jsfiddle.net/Y2XLA/

towr
  • 4,147
  • 3
  • 20
  • 30
  • for first time it works as excepted when length equals to 3. But again when we try to enter in the text box it doesn't works as excepted. And moreover in the last text box it allows to enter more than 3 characters at the intial time itself – Arun Aug 03 '14 at 10:18
  • It works as intended: when the state of the input changes from invalid to valid, the next input gets focus. So as should be expected, changing an earlier (valid) text-box does nothing unless you invalidate that input first (e.g. by erasing everything). In the example the validity is minlength=3, so more than 3 characters are allowed (and besides, there is no mention of disallowing invalid input in the first place.) – towr Aug 03 '14 at 16:27
  • This doesn't find the next input if the inputs are not all at the top level of the form! – Dylanthepiguy Oct 24 '18 at 02:33
3

element.next().focus() might not work if you have a complex form and input are nested into different divs. I ended writing this directive (here I move the focus on Enter, but can be adapted to whatever event):

.directive('enterToTab', function($timeout) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var procAttr = 'data-ett-processed';

      $timeout(function() { // Use $timeout to run the directive when the DOM is fully rendered
        var formElements = element[0].querySelectorAll('input:not([' + procAttr + '="true"]), select:not([' + procAttr + '="true"]), textarea:not([' + procAttr + '="true"])');

        // Run through all elements in form
        var formElementsLength = formElements.length;
        for (var i = 0; i < formElementsLength; i++) {          // Add tabindex attribute
          formElements[i].setAttribute('tabindex', i + 1);

          // Go to next element on Enter key press
          formElements[i].addEventListener('keypress', function(event) {
            if (event.keyCode === 13) { // Enter
              // Prevent Angular from validating all the fields and submitting
              if (event.target.tagName !== 'TEXTAREA') { // Not on textarea, otherwise not possible to add new line
                event.stopPropagation();
                event.preventDefault();
              }

              var nextIndex = parseInt(event.target.getAttribute('tabindex')) + 1;

              // Move focus to next element
              // TODO: find next visible element
              var nextElem = element[0].querySelector('[tabIndex="' + (nextIndex) + '"]');

              if (nextElem) {
                nextElem.focus();
              }
            }
          });
          formElements[i].setAttribute(procAttr, true); // Add attribute to prevent adding 2 listeners on same element
        }
      });
    }
  };
});
Tdy
  • 863
  • 12
  • 28
1
  1. Event should be in HTML component (keypress) = "keyFocus($event)"
  2. Method shoulb be like .ts file.

    keyFocus(input1){ input1.srcElement.nextElementSibling.focus(); }

0

AngularJS already contains a light version of jQuery so you can as well use it... http://docs.angularjs.org/api/angular.element

You could try something like this:

element.next().focus()

mrt
  • 1,669
  • 3
  • 22
  • 32