¿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 se abre un Modal , establezca el foco en una predefinida dentro de este Modal.
  2. Cada vez que vuelve visible (por ejemplo, al hacer clic en algún botón), establece el foco en él.

Traté de lograr el primer requisito con el 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.

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

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

 Name:  

 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 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 scope, y cuando se vuelva verdadera ( element[0].focus() en su controlador ng-click), ejecute el element[0].focus() . Dependiendo de su caso de uso, puede o no necesitar un $ timeout para este:

  
{{ myInput }}

 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 scope 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 scope 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 scope aislado:

 Name:  

 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 scope aislado:

  

 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.

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

Mark Rajcok es el hombre … y su respuesta es válida, pero ha tenido 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í:

  

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 scope $ en los agujeros de forma de evento.

¿Es la mejor solución? No se. Es una 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

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

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

  

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

HTML tiene un autofocus atributo.

  

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

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

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

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 syntax 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.

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

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

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:

  

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:

 
An Input:
Focus!
someFocusVariable: {{ someFocusVariable }}
content: {{ model }}

Aquí está el violín:

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

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:

   

Que hace:

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

Para aquellos que usan Angular con el complemento Bootstrap:

http://angular-ui.github.io/bootstrap/#/modal

Puede conectar con la promesa opened de la instancia modal:

 modalInstance.opened.then(function() { $timeout(function() { angular.element('#title_input').trigger('focus'); }); }); modalInstance.result.then(function ( etc... 

En lugar de crear su propia directiva, es posible simplemente usar las funciones de JavaScript para lograr un enfoque.

Aquí hay un ejemplo.

En el archivo html:

  

En un archivo javascript, en un controlador, por ejemplo, donde desea activar el foco:

 document.getElementById("myInputId").focus(); 

No resucitar a un zombie o conectar mi propia directiva (bueno eso es exactamente lo que estoy haciendo):

https://github.com/hiebj/ng-focus-if

http://plnkr.co/edit/MJS3zRk079Mu72o5A9l6?p=preview

  (function() { 'use strict'; angular .module('focus-if', []) .directive('focusIf', focusIf); function focusIf($timeout) { function link($scope, $element, $attrs) { var dom = $element[0]; if ($attrs.focusIf) { $scope.$watch($attrs.focusIf, focus); } else { focus(true); } function focus(condition) { if (condition) { $timeout(function() { dom.focus(); }, $scope.$eval($attrs.focusDelay) || 0); } } } return { restrict: 'A', link: link }; } })(); 

Primero, una forma oficial de enfocarse es en la hoja de ruta para 1.1 . Mientras tanto, puedes escribir una directiva para implementar el enfoque de configuración.

En segundo lugar, para establecer el foco en un artículo después de que se haya vuelto visible, actualmente se requiere una solución alternativa. Simplemente demore su llamada al elemento focus () con $timeout .

Debido a que existe el mismo problema DOM-controlador para enfocar, desenfocar y seleccionar, propongo tener una directiva ng-target :

   

Hilo angular aquí: http://goo.gl/ipsx4 , y más detalles en el blog aquí: http://goo.gl/4rdZa

La siguiente directiva creará una función .focus() dentro de su controlador según lo especificado por su atributo ng-target . (Crea un .blur() y un .select() también.) Demostración: http://jsfiddle.net/bseib/WUcQX/

Me pareció útil usar una expresión general. De esta forma puedes hacer cosas como mover automáticamente el foco cuando el texto de entrada es válido

  

O enfoca automáticamente cuando el usuario completa un campo de longitud fija

  

Y, por supuesto, enfocar después de la carga

  

El código para la directiva:

 .directive('mooFocusExpression', function ($timeout) { return { restrict: 'A', link: { post: function postLink(scope, element, attrs) { scope.$watch(attrs.mooFocusExpression, function (value) { if (attrs.mooFocusExpression) { if (scope.$eval(attrs.mooFocusExpression)) { $timeout(function () { element[0].focus(); }, 100); //need some delay to work with ng-disabled } } }); } } }; }); 

Si solo deseara un enfoque simple que fuera controlado por un ng-clic.

Html:

   

Directiva:

 'use strict' angular.module('focus',['ng']) .directive('utFocus',function($timeout){ return { link:function(scope,elem,attr){ var focusTarget = attr['utFocus']; scope.$watch(focusTarget,function(value){ $timeout(function(){ elem[0].focus(); }); }); } } }); 

Una simple que funciona bien con modales:

 .directive('focusMeNow', ['$timeout', function ($timeout) { return { restrict: 'A', link: function (scope, element, attrs) { $timeout(function () { element[0].focus(); }); } }; }]) 

Ejemplo

  

Podrías simplemente crear una directiva que fuerce el foco en el elemento decorado en postLinking:

 angular.module('directives') .directive('autoFocus', function() { return { restrict: 'AC', link: function(_scope, _element) { _element[0].focus(); } }; }); 

Luego en tu html:

    

Esto funcionaría para los elementos modales y ng si se alterna, no para ng-show, ya que el postlink solo ocurre en el procesamiento de HTML.

Mark y Blesh tienen excelentes respuestas; sin embargo, Mark tiene un error que Blesh señala (además de ser complejo de implementar), y creo que la respuesta de Blesh tiene un error semántico al crear un servicio específicamente para enviar solicitudes de foco al frontend cuando realmente todo lo que necesitaba era una forma de retrasar el evento hasta que todas las instrucciones estuvieran escuchando.

Así que esto es lo que terminé haciendo, que roba mucho de la respuesta de Blesh, pero mantiene separadas la semántica del evento del controlador y el servicio “después de la carga”.

Esto permite que el evento del controlador se enganche fácilmente para otras cosas además de solo enfocar un elemento específico y también permite incurrir en la sobrecarga de la funcionalidad “después de la carga” solo si es necesario, lo que puede no ser en muchos casos.

Uso

  
 app.controller('MyCtrl', function($scope, afterLoad) { function notifyControllerEvent() { $scope.$broadcast('controllerEvent'); } afterLoad(notifyControllerEvent); }); 

Fuente

 app.directive('focusOn', function() { return function(scope, elem, attr) { scope.$on(attr.focusOn, function(e, name) { elem[0].focus(); }); }; }); app.factory('afterLoad', function ($rootScope, $timeout) { return function(func) { $timeout(func); } }); 

Esto también es posible usar ngModelController . Trabajando con 1.6+ (no sé con versiones anteriores).

HTML

 

JS

 $scope.myForm.myText.$$element.focus(); 

NB: Dependiendo del contexto, quizás tengas que ajustar una función de tiempo de espera.

NB²: cuando se usa un controllerAs , esto es casi lo mismo. Simplemente reemplace name="myForm" con name="vm.myForm" y en JS, vm.myForm.myText.$$element.focus(); .

Probablemente, la solución más simple en la era ES6.

La adición de la siguiente directiva de línea hace que el atributo ‘autofocus’ de HTML sea efectivo en Angular.js.

 .directive('autofocus', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())})) 

Ahora, puedes usar la syntax de autoenfoque HTML5 como:

  

Sólo un novato aquí, pero pude hacer que funcione en un ui.bootstrap.modal con esta directiva:

 directives.directive('focus', function($timeout) { return { link : function(scope, element) { scope.$watch('idToFocus', function(value) { if (value === element[0].id) { $timeout(function() { element[0].focus(); }); } }); } }; }); 

y en el método $ modal.open, utilicé el siguiente para indicar el elemento donde debería ponerse el foco:

 var d = $modal.open({ controller : function($scope, $modalInstance) { ... $scope.idToFocus = "cancelaAteste"; } ... }); 

en la plantilla tengo esto:

  

Edito la directiva focusMe de ‘Mark Rajcok’ para trabajar con enfoque múltiple en un elemento.

HTML:

  

en el Controlador AngularJs:

 $scope.myInputFocus= true; 

Directiva AngulaJS:

 app.directive('focusMe', function ($timeout, $parse) { return { link: function (scope, element, attrs) { var model = $parse(attrs.focusMe); scope.$watch(model, function (value) { if (value === true) { $timeout(function () { scope.$apply(model.assign(scope, false)); element[0].focus(); }, 30); } }); } }; }); 

La siguiente directiva hizo el truco para mí. Utilice el mismo atributo html de enfoque automático para la entrada.

 .directive('autofocus', [function () { return { require : 'ngModel', restrict: 'A', link: function (scope, element, attrs) { element.focus(); } }; }]) 

Si está utilizando modalInstance y tiene el objeto, puede usar “then” para realizar acciones después de abrir el modal. Si no está utilizando modalInstance, y está codificado para abrir el modal, puede usar el evento. El $ timeout no es una buena solución.

Puedes hacer (Bootstrap3):

 $("#" + modalId).on("shown.bs.modal", function() { angular.element("[name='name']").focus(); }); 

En modalInstance puede ver la biblioteca de cómo ejecutar el código después de abrir modal.

No use $ timeout como este, el $ timeout puede ser 0, 1, 10, 30, 50, 200 o más, esto dependerá de la computadora del cliente, y el proceso para abrir modal.

No use $ timeout, deje que el método le indique cuándo puede enfocar;)

¡Espero que esta ayuda! 🙂

Toda la respuesta anterior no funciona si el elemento de enfoque deseado se inyecta en una plantilla de directiva. La siguiente directiva se ajusta tanto al elemento simple como al elemento inyectado por directiva (lo escribí en texto typescript ). acepta selector para elemento enfocable interno. si solo necesita enfocar el elemento self, no envíe ningún parámetro selector a la directiva:

 module APP.Directives { export class FocusOnLoadDirective implements ng.IDirective { priority = 0; restrict = 'A'; constructor(private $interval:any, private $timeout:any) { } link = (scope:ng.IScope, element:JQuery, attrs:any) => { var _self = this; var intervalId:number = 0; var clearInterval = function () { if (intervalId != 0) { _self.$interval.cancel(intervalId); intervalId = 0; } }; _self.$timeout(function(){ intervalId = _self.$interval(function () { let focusableElement = null; if (attrs.focusOnLoad != '') { focusableElement = element.find(attrs.focusOnLoad); } else { focusableElement = element; } console.debug('focusOnLoad directive: trying to focus'); focusableElement.focus(); if (document.activeElement === focusableElement[0]) { clearInterval(); } }, 100); scope.$on('$destroy', function () { // Make sure that the interval is destroyed too clearInterval(); }); }); }; public static factory = ():ng.IDirectiveFactory => { let directive = ($interval:any, $timeout:any) => new FocusOnLoadDirective($interval, $timeout); directive.$inject = ['$interval', '$timeout']; return directive; }; } angular.module('common').directive('focusOnLoad', FocusOnLoadDirective.factory()); 

}

ejemplo de uso para elemento simple:

  

Ejemplo de uso del elemento interno (generalmente para elementos dynamics inyectados como directiva con plantilla):

  

puede usar cualquier selector jQuery en lugar de “entrada”

Es fácil … intente esto

html

  

javascript

 document.getElementById("ddl00").focus(); 

Quiero contribuir a esta discusión después de buscar una mejor solución y no encontrarla, tener que crearla en su lugar.

Criterio: 1. La solución debe ser independiente del scope del controlador principal para boost la reutilización. 2. Evite el uso de $ watch para monitorear alguna condición, esto es lento, aumenta el tamaño del ciclo de digestión y hace que las pruebas sean más difíciles. 3. Evite $ timeout o $ scope. $ Apply () para desencadenar un ciclo digest. 4. Un elemento de entrada está presente en el elemento donde se usa la Directiva abierta.

Esta es la solución que más me gustó:

Directiva:

 .directive('focusInput', [ function () { return { scope: {}, restrict: 'A', compile: function(elem, attr) { elem.bind('click', function() { elem.find('input').focus(); }); } }; }]); 

Html:

  

¡Espero que esto ayude a alguien allá afuera!

Creo que la directiva es innecesaria. Use los atributos de ID y clase de HTML para seleccionar el elemento requerido y haga que el servicio utilice document.getElementById o document.querySelector para aplicar el foco (o jQuery equivalentes).

El marcado es estándar HTML / directivas angulares con id / classes añadidas para la selección

  

El controlador difunde el evento

 $scope.$emit('ui:focus', '#myInput'); 

En el servicio de IU, se usa querySelector: si hay varias coincidencias (digamos por clase), solo devolverá la primera

 $rootScope.$on('ui:focus', function($event, selector){ var elem = document.querySelector(selector); if (elem) { elem.focus(); } }); 

Es posible que desee usar $ timeout () para forzar un ciclo de resumen

Just throwing in some coffee.

 app.directive 'ngAltFocus', -> restrict: 'A' scope: ngAltFocus: '=' link: (scope, el, attrs) -> scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv 

Not sure if relying on the timeout is a good idea, but this works for ng-repeat because this code runs AFTER angularjs updates the DOM, so you make sure all objects are there:

 myApp.directive('onLastRepeat', [function () { return function (scope, element, attrs) { if (scope.$last) setTimeout(function () { scope.$emit('onRepeatLast', element, attrs); }, 1); }; }]); //controller for grid myApp.controller('SimpleController', ['$scope', '$timeout', '$http', function ($scope, $timeout, $http) { var newItemRemoved = false; var requiredAlert = false; //this event fires up when angular updates the dom for the last item //it's observed, so here, we stop the progress bar $scope.$on('onRepeatLast', function (scope, element, attrs) { //$scope.complete(); console.log('done done!'); $("#txtFirstName").focus(); }); }]);