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

759

¿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 , enfóquese en uno predefinido <input>dentro de este Modal.
  2. Cada vez que <input>sea ​​visible (por ejemplo, haciendo clic en algún botón), establezca el foco en él.

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

Cualquier ayuda será apreciada.

Misha Moroshko
fuente

Respuestas:

579
  1. Cuando se abre un Modal, fije el foco en un <input> predefinido dentro de este Modal.

Defina una directiva y haga que $ vea una propiedad / disparador para que sepa 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));
            });
        }
    };
}]);

Saqueador

El tiempo de espera $ parece ser necesario para dar tiempo modal para renderizar.

'2.' Cada vez que <input> se hace visible (por ejemplo, haciendo clic en algún botón), establezca el foco en él.

Crea una directiva esencialmente como la de arriba. Observe algunas propiedades de alcance y, cuando se convierta en verdadero (configúrelo en su controlador ng-click), ejecute element[0].focus(). Dependiendo de su caso de uso, puede necesitar o no un tiempo de espera de $ 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;
          //});
        }
      });
    }
  };
});

Saqueador


Actualización 7/2013 : he visto a algunas personas usar mis directivas de alcance de aislamiento originales y luego tienen problemas con los campos de entrada incrustados (es decir, un campo de entrada en el modal). Una directiva sin un nuevo alcance (o posiblemente un nuevo alcance infantil) debería aliviar algo del dolor. Así que arriba actualicé la respuesta para no usar ámbitos de aislamiento. A continuación se muestra 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 cuando se usa '@', comparamos el valor de activación con "verdadero" ya que @ siempre da como resultado una cadena.

Mark Rajcok
fuente
1
Consulte también la directiva de "enfoque" de @ Josh: stackoverflow.com/a/14859639/215945 No utilizó un alcance aislado en su implementación.
Mark Rajcok
2
@MarkRajcok solo tiene curiosidad por esto: esta versión funciona, pero si configuro un ng-modelen el campo de entrada, el valor del modelo se pierde cuando uso esta directiva con el alcance de aislamiento. El problema no ocurre si pruebo la versión de Josh sin el alcance de aislamiento. Todavía soy un novato, y me gustaría entender la diferencia. Aquí hay un Plunker que lo muestra.
Sunil D.
1
Descubrí que # 1 funciona muy bien con AngularJS 1.0.6. Sin embargo, cuando se ejecuta bajo un depurador, noté que cada vez que descartaba y volvía a abrir mi modal, estaba viendo una llamada más adicional a la función que establece el foco que el tiempo anterior. Modifiqué esa función ligeramente para desvincular el reloj cuando value != "true", y eso pareció solucionar mi problema.
Andrew Brown
1
Entonces ... en una página con estado tuve lo mismo porque $ watch es $ watch y los desarrolladores angulares aman $ watch ... bueno, me topé con un problema, Sr. @ MarkRajcok, un problema que resolví con la solución (básica) que propuesto a continuación.
Ben Lesh
3
hmm para mí, el código funciona, ya que puedo ver que se ejecuta correctamente y el elemento [0] es el control correcto. sin embargo .focus () no activa un foco. posiblemente bootstrap o algo más está interfiriendo con esto?
Sonic Soul
264

(EDITAR: 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 ha tenido un defecto (lo siento Mark) ...

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

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 están intrínsecamente acoplados, 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();
      });
   };
});

Así que ahora puedes usarlo así:

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

y luego en cualquier parte 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 relacionarse con eventos que ya existen. Por otra parte, comienzas a hacer algo inteligente al hacer que diferentes partes de tu aplicación publiquen eventos a los que otras partes de tu aplicación pueden suscribirse.

De todos modos, este tipo de cosas me grita "evento impulsado". Creo que, como desarrolladores de Angular, nos esforzamos mucho por convertir las clavijas en forma de visor en agujeros de forma de evento.

¿Es la mejor solución? No se. Es una solucion.


Solución actualizada

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

Aparte de eso, es el mismo director descrito anteriormente.

Aquí hay una demostración rápida de 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);
    });
  }
});
Ben Lesh
fuente
3
Debe finalizar la llamada $broadcastcon $timeoutsi desea que esto funcione al ingresar al controlador. De lo contrario, es una buena solución.
Shimon Rachlenko
@ShimonRachlenko - Gracias. Pero no estoy seguro de lo que quieres decir con $ timeout. Si quisiera transmitir cuando se procesaba el constructor del controlador, simplemente transmitiría en ese momento. Un tiempo de espera no haría nada más que diferir la transmisión para una ejecución posterior en el bucle de eventos.
Ben Lesh
1
Sí, y eso es suficiente para que la directiva se inicialice. En general, el evento se transmite antes de que la directiva comience a escucharlo. Nuevamente, esto solo es necesario cuando desea activar su directiva cuando ingresa a la página.
Shimon Rachlenko
2
Estás en lo correcto. Supongo que no lo había usado para centrarme en la carga. Actualizaré la respuesta con algo más robusto.
Ben Lesh
1
Esta es, con mucho, la solución más elegante, "angular". Aunque la mayoría de las veces solo copié el código cuando me topé con este problema, ¡me alegra que hayas creado un módulo para ello! Sinceramente, creo que valdría la pena intentar meterlo en el núcleo angular.
Randolpho
241

He encontrado que algunas de las otras respuestas son demasiado complicadas cuando todo lo que realmente necesita 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 se reproduzcan, aunque sea cero, al menos espera eso, de esa manera esto funciona en modales y otras cosas también

ecancil
fuente
1
Habría varias maneras de hacer esto, una forma posible que es realmente sencilla y sencilla sería establecer en el alcance (controlador aquí) la ID del elemento que desea enfocar cuando hace clic en el botón, luego en el directiva simplemente escuche esto. En este caso, no tendría que colocar la directiva en ningún lugar en particular, solo en algún lugar dentro de esa página (he usado este tipo de directivas de observador con éxito en el pasado, particularmente enfocando cosas y desplazando cosas). Entonces, si usted ' re usando jQuery (haría que este simple) acaba de encontrar ese elemento de identificación y enfocarla
ecancil
14
La solución con tiempo de espera cero no funciona para mí si la entrada se encuentra en modo emergente. Pero incluso 10 ms solucionan el problema
Eugene Fidelin
@ecancil: Me gusta su enfoque porque es más simple, pero debe establecer el tiempo de espera en ~ 500 ms en IE porque la animación de la apariencia modal da como resultado un cursor parpadeante fuera de la entrada. No conozco una buena manera de que esto suceda cuando termina la animación, así que la fuerza bruta con los 500 ms.
Dev93
no funcionará, si tiene que extraer más datos de la base de datos, debe esperar a que el controlador complete los datos de extracción, así que agregue suficiente tiempo en lugar de 0
Ali Adravi
1
Para aquellos como @Ade que quieren usar esto con un simple ng-click: Digamos que hacer clic en un botón tiene ng-click="showInput = !showInputen su entrada. Luego, en su entrada real, agregue ng-if="showInput". Al alternar el botón, la directiva se volverá a ejecutar cada vez. Estaba teniendo un problema con esto, ya que estaba usando, ng-showque es el enfoque equivocado.
JVG
91

HTML tiene un atributo autofocus.

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

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

Rayron Victor
fuente
29
Desafortunadamente, esto solo funciona una vez, si es que lo hace. Utilicé esto en una situación de edición angular en el lugar , y funcionó muy bien la primera vez en Chrome, en absoluto en Firefox. En Chrome, cuando hago clic en otro elemento para editarlo en su lugar, o incluso en el mismo elemento nuevamente, ya no se enfoca. Los documentos indican que funciona en la carga de la página, que solo ocurre una vez en una aplicación Angular. ¡Ojalá fuera así de simple, todos estaríamos sentados en una playa ganando un 20%!
Nocturno
62

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

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

JordanC
fuente
2
Sin jquery cargado: angular.forEach (document.querySelectorAll ('. Selector'), function (elem) {elem.focus ();});
Dan Benamy
29
¿No es una mala práctica poner eso en un controlador?
VitalyB
2
Si poner esta línea jqlite dentro de un controlador es una mala práctica, ¿no sería mejor poner esta línea jqlite dentro de una directiva?
Deportes
3
Entiendo esto:Looking up elements via selectors is not supported by jqLite!
Maria Ines Parnisari
1
@VitalyB Tuve un caso en el que necesitaba desenfocar un elemento después de una llamada ajax. Podría agregar solo una línea de JS puro en la devolución de llamada dentro del controlador, o construir una directiva completa exclusivamente para el desenfoque de esa entrada. Sentí que era demasiado complicado, así que decidí por la primera opción.
Sebastianb
55

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

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

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

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

Sanjeev Singh
fuente
No estoy seguro de que sea un gran enfoque para las aplicaciones ViewModel. En angular debe hacerse a través de directivas.
Agat
Por ejemplo, si alguien cambia un cuadro de texto A y necesita mostrar una ventana emergente y establecer el foco en otro cuadro de texto B.
Sanjeev Singh
1
Recibo este error cuando uso esto:Looking up elements via selectors is not supported by jqLite!
JVG
66
Por lo que puedo decir, primero tendrá que cargar jQuery como una dependencia, en cuyo caso se angular.elementconvierte en un contenedor para $() / jQuery(). Entonces, sin eso, esto no funcionará, y básicamente estás usando jQuery de todos modos (pero
corrígeme
@Jascination: jqLite está desarrollado para eliminar la dependencia de jQuery. No necesita jQuery completo para acceder a un elemento en DOM. Pero si tiene instalado jQuery, entonces Angular referirá jQuery. Mira esto: docs.angularjs.org/api/angular.element
Sanjeev Singh
31

No creo que $ timeout sea una buena manera de enfocar el elemento en la creación. Aquí hay un método que utiliza la funcionalidad angular incorporada, extraída de las oscuras profundidades de los documentos angulares. Observe cómo el atributo "link" se puede dividir en "pre" y "post", para las funciones pre-link 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

Cody Moniz
fuente
2
Tuve que ajustar el elemento [0] .focus () en un tiempo de espera $ para que funcione para mí.
codin
@bbodenmiller Mi modal tiene una aplicación de desvanecimiento aplicada, cuando el elemento se construye es invisible (100% transparente), por lo que el navegador bloquea silenciosamente la invocación de foco. Aparentemente, algunos milisegundos pasados ​​permiten enfocarse en una entrada / botón casi transparente pero visible.
snowindy
1
según los documentos, las funciones de "enlace" son "postLink" por defecto. ver también: bennadel.com/blog/…
jody tate
17

Aquí está mi solución original:

saqueador

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 hace visible con ng-show. No hay uso de $ watch o $ aquí.

Edhowler
fuente
De hecho, creo que {{isVisible}} está creando un reloj de todos modos, por lo que la declaración "No usar $ watch" es incorrecta.
masimplo
Sí, creo que tienes razón. Pero aún sirve como una forma fácil de hacerlo.
Edhowler
17

He escrito una directiva de enfoque vinculante bidireccional, al igual que el modelo recientemente.

Puede usar la directiva de enfoque de esta manera:

<input focus="someFocusVariable">

Si realiza alguna variable de alcance VariableFocus trueen cualquier parte de su controlador, la entrada se enfocará. Y si desea "desenfocar" su entrada, someFocusVariable se puede establecer 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/

Umut Benzer
fuente
Esta debería ser la respuesta correcta. Cubre todos los casos de uso.
Kunal
11

Para aquellos que usan Angular con el complemento Bootstrap:

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

Puede engancharse a la openedpromesa de la instancia modal:

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

modalInstance.result.then(function ( etc...
HBP
fuente
¡Bueno! Sin embargo, en mi caso, $timeoutcon 50mses necesario en lugar de 0.
lk_vc
8

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

<button type="button" moo-focus-expression="form.phone.$valid">

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

<button type="submit" moo-focus-expression="smsconfirm.length == 6">

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

<input type="text" moo-focus-expression="true">

El código de 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
                        }
                    }
                });
            }
        }
    };
});
winry
fuente
7

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

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

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

<input focus-if />

(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
        };
    }
})();
Jon Hieb
fuente
Ahora estoy usando esta directiva y funciona muy bien, resuelve el problema general y no es demasiado complejo. Acabé buscando una solución libre de $ timeout. Continúo encontrando comentarios que dicen que el enfoque está "en la hoja de ruta" para Angular 1.1 y 1.2, pero estoy usando 2.xy todavía no manejo el enfoque. Je
tekHedd
6

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

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

Debido a que existe el mismo problema controlador-modifica-DOM para el enfoque, desenfoque y selección, propongo tener una ng-targetdirectiva:

<input type="text" x-ng-model="form.color" x-ng-target="form.colorTarget">
<button class="btn" x-ng-click="form.colorTarget.focus()">do focus</button>

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

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

broc.seib
fuente
+1 para la referencia de la hoja de ruta. De hecho, acabo de verlo en la documentación de 1.2 todavía considerado inestable ( docs.angularjs.org/api/ng.directive:ngFocus )
Edwin Dalorzo
27
@EdwinDalorzo ngFocusparece ser una forma de manejar focuslos eventos, no una forma de establecer el foco en un elemento.
teleclimber
6

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:

<input type="text" id="myInputId" />

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

document.getElementById("myInputId").focus();
usuario3657103
fuente
99
Esta no es una 'forma angular' y no se recomienda a las personas que toquen DOM en sus controladores.
smajl
Es mejor no usar vainilla en Angular, porque Angular no puede rastrearlo ni mantenerlo sincronizado con todo lo demás.
Edgar Quintero
4

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

HTML:

<input ut-focus="focusTigger">

<button ng-click="focusTrigger=!focusTrigger" ng-init="focusTrigger=false"></button>

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

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

<input ng-model="your.value" focus-me-now />
Shawn Dotey
fuente
Perfecto para mi. Gracias
Alexander
3

Simplemente podría crear una directiva que obligue a centrarse 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:

<input type="text" name="first" auto-focus/> <!-- this will get the focus -->
<input type="text" name="second"/>

Esto funcionaría para modals y ng-if elementos alternados, no para ng-show ya que postLinking ocurre solo en el procesamiento HTML.

ibaixas
fuente
3

Mark y Blesh tienen excelentes respuestas; sin embargo, Mark's tiene una falla que señala Blesh (además de ser compleja de implementar), y siento que la respuesta de Blesh tiene un error semántico al crear un servicio que se trata específicamente de enviar una solicitud de enfoque a la interfaz cuando realmente todo lo que necesitaba era una forma de retrasar el evento hasta que todas las directivas estuvieran escuchando.

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

Esto permite que el evento del controlador se enganche fácilmente para otras cosas que no sean 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 serlo en muchos casos.

Uso

<input type="text" focus-on="controllerEvent"/>
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);
  }
});
joshperry
fuente
La única respuesta, que yo (un principiante completo) podía entender. En lugar de "afterLoad (notifyControllerEvent);", solía "notifyControllerEvent ()". De lo contrario, terminó con algunos errores.
Vel Murugan S
3

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

HTML

<form name="myForm">
    <input type="text" name="myText" ng-model="myText">
</form>

JS

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

-

NB: Dependiendo del contexto, es posible que deba ajustar una función de tiempo de espera.

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

Ludovic Guillaume
fuente
3

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

Agregar la siguiente directiva de línea hace que el atributo HTML 'autofocus' sea efectivo en Angular.js.

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

Ahora, puede usar la sintaxis de enfoque automático HTML5 como:

<input type="text" autofocus>
Tsuneo Yoshioka
fuente
@Stephane sí, por lo que entonces se convierte.directive('autofocus', ['$timeout', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())})])
Michael Plautz
2

Solo soy un novato aquí, pero pude hacerlo funcionar 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é lo siguiente para indicar el elemento donde se debe poner el foco:

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

en la plantilla tengo esto:

<input id="myInputId" focus />
pitervergara
fuente
2

La siguiente directiva me sirvió. 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();
        }
    };
}])
Kishore Relangi
fuente
1
obtendrá un error al ejecutar esto a menos que se incluya jQuery, en su lugar debería ser el elemento [0] .focus ()
Ryan M
2

Si está usando modalInstance y tiene el objeto, puede usar "entonces" para realizar acciones después de abrir el modal. Si no está usando 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 mirar la biblioteca para ver cómo ejecutar el código después de abrir modal.

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

No uses $ timeout, deja que el método te diga cuándo puedes concentrarte;)

Espero que esto ayude! :)

Filipe Cotrim Melo
fuente
2

Toda la respuesta anterior no funciona si el elemento de enfoque deseado se inyecta en una plantilla directiva. La siguiente directiva se ajusta tanto al elemento simple como al elemento inyectado por la directiva (lo escribí en mecanografiado ). 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:

<button tabindex="0" focus-on-load />

ejemplo de uso para el elemento interno (generalmente para el elemento inyectado dinámico como la directiva con plantilla):

<my-directive focus-on-load="input" />

puede usar cualquier selector jQuery en lugar de "input"

Ofir Meguri
fuente
2

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

HTML:

<input  focus-me="myInputFocus"  type="text">

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);
                }
            });
        }
    };
});
Mohamad Khani
fuente
1

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

Criterios: 1. La solución debe ser independiente del alcance del controlador principal para aumentar la reutilización. 2. Evite el uso de $ watch para monitorear alguna condición, esto es lento, aumenta el tamaño del bucle de digestión y dificulta las pruebas. 3. Evite $ timeout o $ scope. $ Apply () para activar un bucle de resumen. 4. Un elemento de entrada está presente dentro del elemento donde se utiliza 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:

 <div focus-input>
     <input/>
 </div>

¡Espero que esto ayude a alguien por ahí!

Xiaoma
fuente
Acaba de recrear lo que hace "etiqueta" html, puede reemplazar "div focus-input" con "etiqueta" y deshacerse de la directiva.
primero el
1

Es fácil ... prueba esto

html

<select id="ddl00">  
 <option>"test 01"</option>  
</select>

javascript

document.getElementById("ddl00").focus();
isanka thalagala
fuente
Es correcto fuera de contexto, pero no la forma AngularJS de hacer las cosas
CodeMonkey
1

Si desea establecer el foco en un elemento en particular, puede usar el siguiente enfoque.

  1. Crea un servicio llamado focus.

    angular.module('application')
    .factory('focus', function ($timeout, $window) {
        return function (id) {
            $timeout(function () {
                var element = $window.document.getElementById(id);
                if (element)
                    element.focus();
            });
        };
    });
  2. Inyecte en el controlador desde donde desea llamar.

  3. Llama a este servicio.

Rakesh Burbure
fuente
0

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

El marcado es directivas HTML / angulares estándar con id / clases agregadas para la selección

<input id="myInput" type="text" ng-model="myInputModel" />

Controlador difunde evento

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

En la interfaz de usuario, el servicio utiliza querySelector: si hay varias coincidencias (por ejemplo, debido a la 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

johans
fuente
0

Solo tirando un poco de café.

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