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




formulario campos (25)

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

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

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

Ahora, solo puede usar la sintaxis de autofocus HTML5 como:

<input type="text" autofocus>

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

Requisitos más específicos:

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

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

Cualquier ayuda será apreciada.


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 enfoque:

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

Solo un novato aquí, pero pude hacer que funcionara en un http://angular-ui.github.io/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 usé lo siguiente para indicar el elemento donde se debe poner el enfoque:

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

en la plantilla tengo esto:

<input id="myInputId" focus />

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>

Utilizamos el tiempo de espera para dejar que las cosas en el dom se procesen, aunque sea cero, al menos espera eso; de esa manera, esto funciona en los modales y otras cosas.


Si solo quisieras un enfoque simple que fuera controlado por un clic de 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();
                });
            });
        }
    }
});

En primer lugar, una forma oficial de hacerlo 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, para centrar la atención en un elemento una vez que se ha hecho visible actualmente, se requiere una solución. Solo retrasa tu llamada a element focus () con un $timeout .

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

<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 en el blog aquí: http://goo.gl/4rdZa

La siguiente directiva creará una función .focus() dentro de su controlador como lo especifica su atributo ng-target . (También crea un .blur() y un .select() .) Demostración: http://jsfiddle.net/bseib/WUcQX/


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

Edito la directiva focusMe 'Mark Rajcok' para que funcione para un 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);
                }
            });
        }
    };
});

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 un estilo angular. Jquery juega un rol de forma indirecta y accede directamente a 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.


Todas las respuestas anteriores no funcionan 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 de directiva (lo escribí en escritura de tipos ). Se acepta selector para elemento enfocable interior. Si solo necesita enfocar el elemento propio, no envíe ningún parámetro de 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" />

puedes usar cualquier selector de jQuery en lugar de "entrada"


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

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

Plunker

El $ timeout parece ser necesario para dar el tiempo modal para renderizar.

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

Crear una directiva esencialmente como la de arriba. Observe alguna propiedad del ámbito, y cuando se convierta en 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:

<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 de alcance de aislamiento originales y luego tener problemas con los campos de entrada incrustados (es decir, un campo de entrada en el modo). Una directiva sin nuevo alcance (o posiblemente un nuevo alcance secundario) debería aliviar algo del dolor. Así que arriba actualicé la respuesta para no usar ámbitos aislados. 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 .

Dado que necesitamos restablecer la propiedad trigger / focusInput en la directiva, '=' se usa para el enlace de datos de dos vías. 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.


Una simple que funciona bien con los 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 />

He escrito una directiva de enfoque de enlace bidireccional, como modelo recientemente.

Puede utilizar la directiva de enfoque de esta manera:

<input focus="someFocusVariable">

Si hace que alguna variable de alcance de FocusVariable sea true en cualquier parte de su controlador, la entrada se enfoca. Y si desea "difuminar" su entrada, entonces, algunos FocusVariable se pueden establecer en falso. Es como la primera respuesta de Mark Rajcok pero con enlace de dos vías.

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/


Sólo 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

Mark y Blesh tienen grandes respuestas; sin embargo, Mark tiene un defecto que Blesh señala (además de ser complejo de implementar), y creo que la respuesta de Blesh tiene un error semántico en la creación de un servicio que trata específicamente sobre el envío de solicitudes 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, lo 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 el enfoque de 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

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

(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 enfocarse en la entrada, luego difumine la entrada, luego intente usarlo para enfocar la entrada nuevamente. No funcionará a menos que restablezca el valor booleano en falso, luego $ digerir, y luego restablecerlo en verdadero. Incluso si utiliza una comparación de cadenas en su expresión, se verá obligado a cambiar la cadena a otra cosa, $ digerir, y luego volver a cambiarla. (Esto se ha solucionado con el controlador de eventos de desenfoque).

Así que propongo esta solución alternativa:

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

JavaScript ama los eventos después de todo. Los eventos están intrínsecamente asociados, y aún mejor, evita 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ías vincularte a eventos que ya existen. Por otra parte, empiezas a hacer algo inteligente al tener diferentes partes de tu aplicación que publican eventos a los que otras partes de la aplicación pueden suscribirse.

De todos modos, este tipo de cosas me grita "evento impulsado". Creo que, como desarrolladores de Angular, nos esforzamos mucho para armar clavijas con forma de $ alcance en 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 hacer esto un poco. 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 principio descrito anteriormente.

Aquí está una rápida demostración 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);
    });
  }
});

Simplemente puede crear una directiva que obligue a centrarse en el elemento decorado en el enlace posterior:

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 los modales y ng-if elementos alternados, no para ng-show, ya que el enlace posterior ocurre solo en el procesamiento de HTML.


Si está usando modalInstance y tiene el objeto, puede usar "luego" para realizar acciones después de abrir el modal. Si no está utilizando la 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 consultar la biblioteca para ver 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 cliente y del proceso para abrir el modal.

No use $ timeout. Deje que el método le indique cuándo puede concentrarse;)

Espero que esta ayuda! :)


No estoy seguro de si confiar en el tiempo de espera es una buena idea, pero esto funciona para ng-repeat porque este código se ejecuta DESPUÉS de que angularjs actualice el DOM, así que asegúrese de que todos los objetos estén allí:

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

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


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

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


Me ha resultado útil utilizar una expresión general. De esta manera, puedes hacer cosas como mover el foco automáticamente 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 foco después de 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
                        }
                    }
                });
            }
        }
    };
});

Esto también es posible utilizar ngModelController . Trabajando con 1.6+ (no 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 tenga que ajustar una función de tiempo de espera.

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


La siguiente directiva hizo el truco para mí. Use el mismo atributo html de autoenfoque para la entrada.

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

Usted está mirando ng-focus y ng-blur .

<input type="text" style="border: none" ng-model="model" ng-focus="A()" ng-blur="B()">

En una nota al margen, use clases de CSS en lugar de estilos en línea ... :)

O simplemente llame al mismo método con argumento y establezca el valor como: -

 <input type="text" style="border: none" ng-model="model" ng-focus="A(true)" ng-blur="A(false)">




angularjs angularjs-directive