Pregunta ¿Cómo establecer el foco en el campo de entrada?


¿Cuál es la "forma angular" para establecer el foco en el campo de entrada en AngularJS?

Requisitos más específicos:

  1. Cuando un Modal se abre, establece el foco en un predefinido <input> dentro de este Modal.
  2. Cada vez <input> se vuelve visible (por ejemplo, al hacer clic en un botón), establece el foco en él.

Traté de lograr el primer requisito con autofocus, pero esto solo funciona cuando el Modal se abre por primera vez, y solo en ciertos navegadores (por ejemplo, en Firefox no funciona).

Cualquier ayuda será apreciada.


713
2018-02-12 13:08


origen


Respuestas:


  1. Cuando se abre un Modal, establezca el foco en una <input> predefinida dentro de este Modal.

Defina una directiva y pida $ ver una propiedad / activador para saber cuándo enfocar el elemento:

Name: <input type="text" focus-me="shouldBeOpen">

app.directive('focusMe', ['$timeout', '$parse', function ($timeout, $parse) {
    return {
        //scope: true,   // optionally create a child scope
        link: function (scope, element, attrs) {
            var model = $parse(attrs.focusMe);
            scope.$watch(model, function (value) {
                console.log('value=', value);
                if (value === true) {
                    $timeout(function () {
                        element[0].focus();
                    });
                }
            });
            // to address @blesh's comment, set attribute value to 'false'
            // on blur event:
            element.bind('blur', function () {
                console.log('blur');
                scope.$apply(model.assign(scope, false));
            });
        }
    };
}]);

Plunker

El $ timeout parece ser necesario para dar la hora modal para procesar.

'2.' Cada vez que <input> se vuelve visible (por ejemplo, al hacer clic en algún botón), establece el foco en él.

Crea una directiva esencialmente como la de arriba. Observe alguna propiedad del alcance, y cuando se vuelva verdadera (configúrela en su controlador ng-click), ejecute element[0].focus(). Dependiendo de su caso de uso, puede o no necesitar un $ timeout para este:

<button class="btn" ng-click="showForm=true; focusInput=true">show form and
 focus input</button>
<div ng-show="showForm">
  <input type="text" ng-model="myInput" focus-me="focusInput"> {{ myInput }}
  <button class="btn" ng-click="showForm=false">hide form</button>
</div>

app.directive('focusMe', function($timeout) {
  return {
    link: function(scope, element, attrs) {
      scope.$watch(attrs.focusMe, function(value) {
        if(value === true) { 
          console.log('value=',value);
          //$timeout(function() {
            element[0].focus();
            scope[attrs.focusMe] = false;
          //});
        }
      });
    }
  };
});

Plunker


Actualización 7/2013: He visto a algunas personas usar mis directivas originales de alcance aislado y luego tienen problemas con los campos de entrada incrustados (es decir, un campo de entrada en el modal). Una directiva sin ámbito nuevo (o posiblemente un nuevo alcance infantil) debería aliviar parte del dolor. Así que arriba he actualizado la respuesta para no usar ámbitos aislados. A continuación está la respuesta original:

Respuesta original para 1., usando un alcance aislado:

Name: <input type="text" focus-me="{{shouldBeOpen}}">

app.directive('focusMe', function($timeout) {
  return {
    scope: { trigger: '@focusMe' },
    link: function(scope, element) {
      scope.$watch('trigger', function(value) {
        if(value === "true") { 
          $timeout(function() {
            element[0].focus(); 
          });
        }
      });
    }
  };
});

Plunker.

Respuesta original para 2., usando un alcance aislado:

<button class="btn" ng-click="showForm=true; focusInput=true">show form and
 focus input</button>
<div ng-show="showForm">
  <input type="text" focus-me="focusInput">
  <button class="btn" ng-click="showForm=false">hide form</button>
</div>

app.directive('focusMe', function($timeout) {
  return {
    scope: { trigger: '=focusMe' },
    link: function(scope, element) {
      scope.$watch('trigger', function(value) {
        if(value === true) { 
          //console.log('trigger',value);
          //$timeout(function() {
            element[0].focus();
            scope.trigger = false;
          //});
        }
      });
    }
  };
});

Plunker.

Como necesitamos restablecer la propiedad trigger / focusInput en la directiva, '=' se usa para el enlace de datos bidireccional. En la primera directiva, '@' era suficiente. También tenga en cuenta que al usar '@', comparamos el valor de activación con "verdadero", ya que @ siempre da como resultado una cadena.


548
2018-02-12 16:23



(EDIT: he agregado una solución actualizada debajo de esta explicación)

Mark Rajcok es el hombre ... y su respuesta es una respuesta válida, pero tiene tenía un defecto (lo siento Mark) ...

... Trate de usar el booleano para enfocar la entrada, luego difumine la entrada, luego intente usarla para enfocar la entrada nuevamente. No funcionará a menos que restablezca el valor booleano a falso, luego $ digest, luego reinícielo a verdadero. Incluso si usa una comparación de cadenas en su expresión, se verá obligado a cambiar la cadena por otra cosa, $ digest, y luego volver a cambiarla. (Esto se ha solucionado con el controlador de eventos blur).

Entonces propongo esta solución alternativa:

Use un evento, la característica olvidada de Angular.

JavaScript ama los eventos después de todo. Los eventos son inherentemente poco parejos, y aún mejor, evitas agregar otro $ watch a tu $ digest.

app.directive('focusOn', function() {
   return function(scope, elem, attr) {
      scope.$on(attr.focusOn, function(e) {
          elem[0].focus();
      });
   };
});

Entonces ahora podrías usarlo así:

<input type="text" focus-on="newItemAdded" />

y luego en cualquier lugar de tu aplicación ...

$scope.addNewItem = function () {
    /* stuff here to add a new item... */

    $scope.$broadcast('newItemAdded');
};

Esto es increíble porque puedes hacer todo tipo de cosas con algo como esto. Por un lado, podría vincularse a eventos que ya existen. Por otro lado, comienzas a hacer algo inteligente haciendo que diferentes partes de tu aplicación publiquen eventos a los que se pueden suscribir otras partes de tu aplicación.

De todos modos, este tipo de cosas grita "evento impulsado" para mí. Creo que, como desarrolladores de Angular, tratamos de dificultar enormemente las clavijas en forma de alcance $ en los agujeros de forma de evento.

¿Es la mejor solución? No se. Sus un solución.


Solución actualizada

Después del comentario de @ ShimonRachlenko a continuación, he cambiado mi método de hacerlo un poco. Ahora utilizo una combinación de un servicio y una directiva que maneja un evento "detrás de escena":

Aparte de eso, es el mismo principio descrito anteriormente.

Aquí hay una demo rápida Plunk

Uso

<input type="text" focus-on="focusMe"/>
app.controller('MyCtrl', function($scope, focus) {
    focus('focusMe');
});

Fuente

app.directive('focusOn', function() {
   return function(scope, elem, attr) {
      scope.$on('focusOn', function(e, name) {
        if(name === attr.focusOn) {
          elem[0].focus();
        }
      });
   };
});

app.factory('focus', function ($rootScope, $timeout) {
  return function(name) {
    $timeout(function (){
      $rootScope.$broadcast('focusOn', name);
    });
  }
});

255
2017-08-18 03:30



He encontrado que algunas de las otras respuestas son demasiado complicadas cuando todo lo que realmente necesitas es esto

app.directive('autoFocus', function($timeout) {
    return {
        restrict: 'AC',
        link: function(_scope, _element) {
            $timeout(function(){
                _element[0].focus();
            }, 0);
        }
    };
});

el uso es

<input name="theInput" auto-focus>

Usamos el tiempo de espera para dejar que las cosas en el dom render, aunque es cero, al menos espera eso - de esa manera esto funciona en modales y otras cosas


231
2018-01-01 02:25



HTML tiene un atributo autofocus.

<input type="text" name="fname" autofocus>

http://www.w3schools.com/tags/att_input_autofocus.asp


82
2017-09-29 12:43



También puede usar la funcionalidad jqlite integrada en angular.

angular.element('.selector').trigger('focus');


56
2017-12-11 08:35



Esto funciona bien y es una forma angular de enfocar el control de entrada

angular.element('#elementId').focus()

Aunque no es una forma angular pura de realizar la tarea, la sintaxis sigue el estilo angular. Jquery juega un papel indirecto y accede directamente al DOM usando Angular (jQLite => JQuery Light).

Si es necesario, este código se puede colocar fácilmente dentro de una directiva angular simple donde el elemento es directamente accesible.


49
2017-08-11 07:10



No creo que $ timeout sea una buena manera de enfocar el elemento en la creación. Aquí hay un método que utiliza la función angular incorporada, excavada en las profundidades oscuras de los documentos angulares. Observe cómo el atributo "link" se puede dividir en "pre" y "post", para funciones previas al enlace y post-link.

Ejemplo de trabajo: http://plnkr.co/edit/Fj59GB

// this is the directive you add to any element you want to highlight after creation
Guest.directive('autoFocus', function() {
    return {
        link: {
            pre: function preLink(scope, element, attr) {
                console.debug('prelink called');
                // this fails since the element hasn't rendered
                //element[0].focus();
            },
            post: function postLink(scope, element, attr) {
                console.debug('postlink called');
                // this succeeds since the element has been rendered
                element[0].focus();
            }
        }
    }
});
<input value="hello" />
<!-- this input automatically gets focus on creation -->
<input value="world" auto-focus />

Documentos completos de la Directiva AngularJS: https://docs.angularjs.org/api/ng/service/$compile


30
2018-05-28 19:07



He escrito una directiva de enfoque vinculante de dos vías, al igual que el modelo recientemente.

Usas la directiva de enfoque de esta manera:

<input focus="someFocusVariable">

Si hace que alguna variable de ámbito de FocusVariable sea verdadera en cualquier parte de su controlador, la entrada obtuvo el foco. Y si "desenfoca" su entrada, algunosFocusVariable se establece en falso. Es como la primera respuesta de Mark Rajcok pero con enlace bidireccional.

Aquí está la directiva:

function Ctrl($scope) {
  $scope.model = "ahaha"
  $scope.someFocusVariable = true; // If you want to focus initially, set this to true. Else you don't need to define this at all.
}

angular.module('experiement', [])
  .directive('focus', function($timeout, $parse) {
    return {
      restrict: 'A',
      link: function(scope, element, attrs) {
          scope.$watch(attrs.focus, function(newValue, oldValue) {
              if (newValue) { element[0].focus(); }
          });
          element.bind("blur", function(e) {
              $timeout(function() {
                  scope.$apply(attrs.focus + "=false"); 
              }, 0);
          });
          element.bind("focus", function(e) {
              $timeout(function() {
                  scope.$apply(attrs.focus + "=true");
              }, 0);
          })
      }
    }
  });

Uso:

<div ng-app="experiement">
  <div ng-controller="Ctrl">
    An Input: <input ng-model="model" focus="someFocusVariable">
    <hr>
        <div ng-click="someFocusVariable=true">Focus!</div>  
        <pre>someFocusVariable: {{ someFocusVariable }}</pre>
        <pre>content: {{ model }}</pre>
  </div>
</div>

Aquí está el violín:

http://fiddle.jshell.net/ubenzer/9FSL4/8/


18
2017-07-19 06:49



Aquí está mi solución original:

plunker

var app = angular.module('plunker', []);
app.directive('autoFocus', function($timeout) {
    return {
        link: function (scope, element, attrs) {
            attrs.$observe("autoFocus", function(newValue){
                if (newValue === "true")
                    $timeout(function(){element[0].focus()});
            });
        }
    };
});

Y el HTML:

<button ng-click="isVisible = !isVisible">Toggle input</button>
<input ng-show="isVisible" auto-focus="{{ isVisible }}" value="auto-focus on" />

Que hace:

Enfoca la entrada a medida que se vuelve visible con ng-show. No uso de $ watch o $ on aquí.


17
2018-06-11 20:58