schacki
schacki

Reputation: 9533

AngularJS: Move to next form input element after successful validation

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

Upvotes: 4

Views: 14804

Answers (4)

Nadimuthu Sarangapani
Nadimuthu Sarangapani

Reputation: 316

  1. Event should be in HTML component (keypress) = "keyFocus($event)"
  2. Method shoulb be like .ts file.

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

Upvotes: 1

Tdy
Tdy

Reputation: 1013

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
        }
      });
    }
  };
});

Upvotes: 3

towr
towr

Reputation: 4157

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/

Upvotes: 5

mrt
mrt

Reputation: 1749

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()

Upvotes: 0

Related Questions