Come impostare il focus sul campo di input?


759

Qual è il "modo angolare" per mettere a fuoco il campo di input in AngularJS?

Requisiti più specifici:

  1. Quando viene aperto un modale , focalizzare l'attenzione su un predefinito <input>all'interno di questo modale.
  2. Ogni volta che <input>diventa visibile (ad esempio facendo clic su un pulsante), impostare lo stato attivo su di esso.

Ho provato a raggiungere il primo requisito con autofocus, ma questo funziona solo quando Modal viene aperto per la prima volta e solo in alcuni browser (ad esempio in Firefox non funziona).

Qualsiasi aiuto sarà apprezzato.

Risposte:


579
  1. Quando viene aperto un modale, focalizzare l'attenzione su un <input> predefinito all'interno di questo modale.

Definisci una direttiva e falla $ guardare una proprietà / trigger in modo che sappia quando focalizzare l'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

Il timeout $ sembra essere necessario per dare il tempo modale per il rendering.

'2.' Ogni volta che <input> diventa visibile (ad esempio facendo clic su un pulsante), impostare lo stato attivo su di esso.

Crea una direttiva essenzialmente come quella sopra. Guarda alcune proprietà dell'ambito e quando diventa vero (impostalo nel tuo gestore ng-click), esegui element[0].focus(). A seconda del caso d'uso, potrebbe essere necessario o meno un timeout di $ per questo:

<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


Aggiornamento 7/2013 : ho visto alcune persone usare le mie direttive originali dell'ambito di isolamento e quindi avere problemi con i campi di input incorporati (ovvero un campo di input nel modale). Una direttiva senza un nuovo campo di applicazione (o forse un nuovo campo di applicazione per i minori) dovrebbe alleviare parte del dolore. Quindi sopra ho aggiornato la risposta per non usare gli ambiti isolati. Di seguito è la risposta originale:

Risposta originale per 1., utilizzando un ambito isolato:

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 .

Risposta originale per 2., usando un ambito isolato:

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

Poiché è necessario reimpostare la proprietà trigger / focusInput nella direttiva, '=' viene utilizzato per il database bidirezionale. Nella prima direttiva, "@" era sufficiente. Si noti inoltre che quando si utilizza '@' si confronta il valore di trigger con "true" poiché @ risulta sempre in una stringa.


1
Vedi anche la direttiva "focus" di @Josh: stackoverflow.com/a/14859639/215945 Non ha utilizzato un ambito di isolamento nella sua implementazione.
Mark Rajcok,

2
@MarkRajcok è solo curioso di questo: questa versione funziona ma se imposto un ng-modelcampo di input, il valore del modello viene perso quando uso questa direttiva con l'ambito isolato. Il problema non si verifica se provo la versione di Josh senza l'ambito di isolamento. Ancora un novizio, e mi piacerebbe capire la differenza. Ecco un Plunker che lo mostra.
Sunil D.

1
Ho scoperto che il numero 1 funziona molto bene con AngularJS 1.0.6. Tuttavia, durante l'esecuzione con un debugger, ho notato che ogni volta che ho respinto e riaperto il mio modale, stavo vedendo un'ulteriore chiamata alla funzione che imposta lo stato attivo rispetto al tempo precedente. Ho modificato leggermente quella funzione per sciogliere l'orologio quando value != "true"e che sembrava risolvere il mio problema.
Andrew Brown,

1
Quindi ... su una pagina stateful ho avuto la stessa cosa perché $ watch è $ watch e gli sviluppatori angolari adorano $ watch ... beh, ho riscontrato un problema, Mr @MarkRajcok, un problema che ho risolto con la soluzione (di base) che ho proposto di seguito.
Ben Lesh,

3
hmm per me il codice funziona, come in posso vederlo eseguire correttamente ed element [0] è il controllo giusto. tuttavia .focus () non attiva un focus. forse bootstrap o qualcos'altro sta interferendo con questo?
Sonic Soul,

264

(EDIT: ho aggiunto una soluzione aggiornata sotto questa spiegazione)

Mark Rajcok è l'uomo ... e la sua risposta è una risposta valida, ma l' ha fatto avuto un difetto (scusate Mark) ...

... Prova a utilizzare il valore booleano per mettere a fuoco l'input, quindi sfoca l'input, quindi prova a utilizzarlo per focalizzare nuovamente l'input. Non funzionerà se non ripristini il valore booleano su false, quindi $ digest, quindi ripristinalo su true. Anche se usi un confronto di stringhe nella tua espressione, sarai costretto a cambiare la stringa in qualcos'altro, $ digest, per poi cambiarla.(Questo è stato risolto con il gestore di eventi di sfocatura.)

Quindi propongo questa soluzione alternativa:

Usa un evento, la caratteristica dimenticata di Angular.

JavaScript ama gli eventi dopo tutto. Gli eventi sono intrinsecamente vagamente accoppiati e, ancora meglio, eviti di aggiungere un altro $ watch al tuo $ digest.

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

Quindi ora potresti usarlo in questo modo:

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

e poi ovunque nella tua app ...

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

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

È fantastico perché puoi fare qualsiasi cosa con qualcosa del genere. Per uno, potresti legare ad eventi già esistenti. Inoltre, inizi a fare qualcosa di intelligente facendo in modo che diverse parti della tua app pubblichino eventi a cui altre parti della tua app possono iscriversi.

Ad ogni modo, questo tipo di cose urla "event driven" per me. Penso che come sviluppatori angolari ci sforziamo davvero di martellare i pioli a forma di $ scope in buchi a forma di evento.

È la soluzione migliore? Non so. È una soluzione.


Soluzione aggiornata

Dopo il commento di @ ShimonRachlenko di seguito, ho modificato leggermente il mio metodo per farlo. Ora uso una combinazione di un servizio e una direttiva che gestisce un evento "dietro le quinte":

A parte questo, è lo stesso principio descritto sopra.

Ecco una demo veloce Plunk

uso

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

fonte

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

3
È necessario concludere la chiamata $broadcastcon $timeoutse si desidera che funzioni per accedere al controller. Altrimenti bella soluzione.
Shimon Rachlenko,

@ShimonRachlenko - Grazie. Ma non sono sicuro di cosa intendi per $ timeout. Se volevo trasmettere quando il costruttore del controller era in fase di elaborazione, avrei semplicemente trasmesso proprio in quel momento. Un timeout non farebbe altro che rimandare la trasmissione a una successiva esecuzione nel loop degli eventi.
Ben Lesh,

1
Sì, e questo è sufficiente per l'inizializzazione della direttiva. In caso contrario, l'evento viene trasmesso prima che la direttiva inizi ad ascoltarlo. Ancora una volta, questo è necessario solo quando si desidera attivare la direttiva quando si accede alla pagina.
Shimon Rachlenko

2
Hai ragione. Immagino di non averlo usato per concentrarmi sul carico. Aggiornerò la risposta con qualcosa di più robusto.
Ben Lesh

1
Questa è di gran lunga la soluzione più elegante e "angolare". Anche se ho appena copiato il codice per la prima volta quando ho riscontrato questo problema, sono contento che tu l'abbia creato un modulo! Sinceramente penso che valga la pena tentare di inserirlo nell'angolare del nucleo.
Randolpho,

241

Ho trovato alcune delle altre risposte troppo complicate quando tutto ciò di cui hai veramente bisogno è questo

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

l'utilizzo è

<input name="theInput" auto-focus>

Usiamo il timeout per consentire il rendering delle cose nel dom, anche se è zero, almeno lo aspetta - in questo modo funziona in modali e quant'altro


1
Ci sarebbero diversi modi per farlo, un possibile modo che sia davvero diretto e facile sarebbe che l'ambito (controller qui) imposta l'ID dell'elemento su cui vuoi concentrarti quando fai clic sul pulsante, quindi nella direttiva semplicemente ascolta questo. In questo caso non dovresti collocare la direttiva in alcun luogo in particolare, solo da qualche parte all'interno di quella pagina (ho usato questo tipo di direttive sugli osservatori con successo in passato, in particolare con la messa a fuoco delle cose e lo scorrimento delle cose) - Quindi se tu ' riutilizzando jquery (lo renderebbe più semplice) basta trovare quell'elemento id e focalizzarlo
ecancil

14
La soluzione con timeout zero non funziona per me se l'input si trova nel popup modale. Ma anche 10 ms risolvono il problema
Eugene Fidelin,

@ecancil: mi piace il tuo approccio perché è più semplice, ma devi impostare il timeout su ~ 500ms in IE perché l'animazione dell'aspetto modale si traduce in un cursore lampeggiante all'esterno dell'input. Non conosco un bel modo per far sì che ciò accada quando l'animazione termina, quindi la forza bruta con i 500ms.
Dev93,

non funzionerà, se devi estrarre più dati dal database, devi aspettare che il controller completi i dati di pull, quindi aggiungi abbastanza tempo al posto di 0
Ali Adravi

1
Per quelli come @Ade che vogliono usarlo con un semplice ng-click: diciamo che fare clic su un pulsante ha ng-click="showInput = !showInputsul tuo input. Quindi, sul tuo input effettivo, aggiungi ng-if="showInput". Se si preme il pulsante, la direttiva verrà rieseguita ogni volta. Stavo avendo un problema con questo mentre stavo usando ng-showquale è l'approccio sbagliato.
JVG,

91

HTML ha un attributo autofocus.

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

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


29
Sfortunatamente, questo funziona solo una volta se non del tutto. L'ho usato in una situazione di modifica angolare sul posto , e ha funzionato benissimo la prima volta in Chrome, per niente in Firefox. In Chrome, quando faccio clic su un altro elemento per modificarlo sul posto, o anche di nuovo sullo stesso elemento, non si attiva più. I documenti affermano che funziona al caricamento della pagina, cosa che accade una sola volta in un'app Angolare. Se solo fosse così semplice, saremmo tutti seduti su una spiaggia a guadagnare il 20%!
Notturno,

62

Puoi anche usare la funzionalità jqlite integrata in angolare.

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


2
Senza jquery caricato: angular.forEach (document.querySelectorAll ('. Selector'), function (elem) {elem.focus ();});
Dan Benamy,

29
Non è una cattiva pratica metterlo in un controller?
VitalyB,

2
Se mettere questa linea jqlite all'interno di un controller è una cattiva pratica, non sarebbe meglio inserire questa linea jqlite all'interno di una direttiva?
sport

3
Ho capito:Looking up elements via selectors is not supported by jqLite!
Maria Ines Parnisari,

1
@VitalyB Ho avuto un caso in cui dovevo sfocare un elemento dopo una chiamata Ajax. Potrei aggiungere solo una linea di puro JS nel callback all'interno del controller, o costruire un'intera direttiva esclusivamente per la sfocatura di quell'input. Ho solo pensato che fosse una seccatura, quindi ho optato per la prima opzione.
Sebastianb,

55

Questo funziona bene e un modo angolare per focalizzare il controllo dell'input

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

Questo non è un modo puramente angolare di svolgere il compito, ma la sintassi segue lo stile angolare. Jquery svolge un ruolo indirettamente e accede direttamente al DOM utilizzando Angular (jQLite => JQuery Light).

Se necessario, questo codice può essere facilmente inserito in una semplice direttiva angolare in cui l'elemento è direttamente accessibile.


Non sono sicuro che sia un ottimo approccio per le app ViewModel. In angolare deve essere fatto tramite direttive.
Agat,

Ad esempio, se qualcuno cambia una casella di testo A e devi mostrare il pop-up e concentrarti su un'altra casella di testo B.
Sanjeev Singh

1
Ricevo questo errore quando lo utilizzo:Looking up elements via selectors is not supported by jqLite!
JVG,

6
Per quanto ne so, dovrai prima caricare jQuery come dipendenza, nel qual caso angular.elementdiventa un wrapper per $() / jQuery(). Quindi senza questo non funzionerà, e praticamente stai usando jQuery comunque (ma correggimi se sbaglio)
JVG

@Jascination: jqLite è stato sviluppato per rimuovere la dipendenza jQuery. Non è necessario l'intero jQuery per accedere a un elemento in DOM. Ma se hai installato jQuery, Angular farà riferimento a jQuery. Dai un'occhiata a
Sanjeev Singh,

31

Non credo che $ timeout sia un buon modo per focalizzare l'elemento sulla creazione. Ecco un metodo che utilizza la funzionalità angolare incorporata, estratta dalle profondità oscure dei documenti angolari. Notare come l'attributo "collegamento" può essere suddiviso in "pre" e "post", per le funzioni pre-link e post-link.

Esempio di lavoro: 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 />

Documenti della direttiva Full AngularJS: https://docs.angularjs.org/api/ng/service/$compile


2
Ho dovuto racchiudere l'elemento [0] .focus () in un timeout $ per farlo funzionare per me.
codin

@bbodenmiller Il mio modale ha una cosa sfumata applicata, quando l'elemento viene costruito è invisibile (100% trasparente), quindi i blocchi del browser focalizzano l'invocazione silenziosamente. Apparentemente alcuni millisecondi passati consentono di concentrarsi su input / pulsante quasi trasparenti ma visibili.
snowindy,

1
per i documenti, le funzioni "link" sono "postLink" per impostazione predefinita. vedi anche: bennadel.com/blog/…
jody tate

17

Ecco la mia soluzione originale:

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

E l'HTML:

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

Cosa fa:

Mette a fuoco l'ingresso non appena diventa visibile con ng-show. Nessun uso di $ watch o $ qui.


In realtà credo che {{isVisible}} stia comunque creando un orologio, quindi l'istruzione "No use of $ watch" non è corretta.
Masimplo,

Sì, penso che tu abbia ragione. Ma serve ancora come un modo semplice per farlo.
Edhowler,

17

Ho scritto una direttiva di messa a fuoco vincolante a due vie, proprio come modello di recente.

Puoi usare la direttiva focus in questo modo:

<input focus="someFocusVariable">

Se rendi variabile scope alcuniFocusVariable true in qualsiasi punto del controller, l'input viene focalizzato. E se vuoi "sfocare" il tuo input, someFocusVariable può essere impostato su false. È come la prima risposta di Mark Rajcok ma con rilegatura a due vie.

Ecco la direttiva:

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>

Ecco il violino:

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


Questa dovrebbe essere la risposta corretta. Copre tutti i casi d'uso.
Kunal,

11

Per coloro che usano Angular con il plug-in Bootstrap:

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

Puoi agganciarti alla openedpromessa dell'istanza modale:

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

modalInstance.result.then(function ( etc...

Buona! Tuttavia, nel mio caso, $timeoutcon 50msè necessario invece di 0.
lk_vc,

8

Ho trovato utile usare un'espressione generale. In questo modo puoi fare cose come spostare automaticamente lo stato attivo quando il testo di input è valido

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

Oppure si concentra automaticamente quando l'utente completa un campo di lunghezza fissa

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

E ovviamente concentrarsi dopo il caricamento

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

Il codice per la direttiva:

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

7

Non resuscitare uno zombi o collegare la mia direttiva (ok, è esattamente quello che sto facendo):

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

Ora sto usando questa direttiva e funziona benissimo, risolve il problema generale e non è eccessivamente complesso. Lasciati andare alla ricerca di una soluzione gratuita per $ timeout. Continuate a trovare commenti dicendo che il focus è "sulla roadmap" per Angular 1.1 e 1.2, ma sto usando 2.x e ancora nessuna gestione del focus. Eh.
tekHedd,

6

Innanzitutto, un modo ufficiale per concentrarsi è sulla tabella di marcia per 1.1 . Nel frattempo, puoi scrivere una direttiva per implementare l'impostazione focus.

In secondo luogo, per mettere a fuoco un elemento dopo che è diventato visibile attualmente richiede una soluzione alternativa. Basta ritardare la chiamata a element focus () con a $timeout.

Poiché esiste lo stesso problema di controller-modifica-DOM per focus, sfocatura e selezione, propongo di avere una ng-targetdirettiva:

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

Discussione angolare qui: http://goo.gl/ipsx4 e ulteriori dettagli sul blog qui: http://goo.gl/4rdZa

La seguente direttiva creerà una .focus()funzione all'interno del controller come specificato dall'attributo ng-target. (Crea un .blur()e .select()anche un .) Demo: http://jsfiddle.net/bseib/WUcQX/


+1 per il riferimento della roadmap. In effetti l'ho appena visto nella documentazione dell'1.2 ancora considerata instabile ( docs.angularjs.org/api/ng.directive:ngFocus )
Edwin Dalorzo,

27
@EdwinDalorzo ngFocussembra essere un modo per gestire focusgli eventi, non un modo di impostare la messa a fuoco su un elemento.
Teleclimber,

6

Invece di creare la tua direttiva, è possibile utilizzare semplicemente le funzioni javascript per raggiungere un focus.

Ecco un esempio

Nel file html:

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

In un file javascript, ad esempio in un controller, in cui si desidera attivare lo stato attivo:

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

9
Questo non è un "modo angolare" e non consiglia alle persone di toccare DOM nei loro controller.
smajl,

Meglio non usare la vaniglia in Angular, perché Angular non può seguirla né mantenerla sincronizzata con tutto il resto.
Edgar Quintero,

4

Se volevi solo un semplice focus controllato da un clic.

html:

<input ut-focus="focusTigger">

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

Direttiva:

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

4

Un semplice che funziona bene con i modali:

.directive('focusMeNow', ['$timeout', function ($timeout)
{
    return {
        restrict: 'A',

        link: function (scope, element, attrs)
        {


            $timeout(function ()
            {
                element[0].focus();
            });



        }
    };
}])

Esempio

<input ng-model="your.value" focus-me-now />

Perfetto per me. Grazie
Alexander

3

Potresti semplicemente creare una direttiva che costringa a concentrarsi sull'elemento decorato su postLinking:

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

Quindi nel tuo HTML:

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

Questo funzionerebbe per gli elementi modali e ng-if attivati, non per ng-show poiché postLinking avviene solo sull'elaborazione HTML.


3

Mark e Blesh hanno grandi risposte; tuttavia, Mark's ha un difetto che Blesh evidenzia (oltre ad essere complesso da implementare), e sento che la risposta di Blesh ha un errore semantico nel creare un servizio che riguarda specificamente l'invio di richieste di focus al frontend quando in realtà tutto ciò di cui aveva bisogno era un modo per ritardare l'evento fino a quando tutte le direttive fossero in ascolto.

Quindi, ecco cosa ho finito per rubare molto dalla risposta di Blesh, ma separa la semantica dell'evento controller e il servizio "after load".

Ciò consente all'evento controller di essere facilmente agganciato per cose diverse dalla semplice messa a fuoco di un elemento specifico e consente anche di sostenere l'overhead della funzionalità "after load" solo se necessario, cosa che potrebbe non essere in molti casi.

uso

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

  afterLoad(notifyControllerEvent);
});

fonte

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

L'unica e sola risposta, che io (un principiante assoluto) potrei capire. Invece di "afterLoad (notifyControllerEvent);", ho usato "notifyControllerEvent ()". Altrimenti, si è concluso con alcuni errori.
Vel Murugan S

3

Questo è anche possibile usare ngModelController. Lavorare con 1.6+ (non lo so con le versioni precedenti).

HTML

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

JS

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

-

NB: A seconda del contesto, potresti dover ricorrere a una funzione di timeout.

NB²: quando si utilizza controllerAs, questo è quasi lo stesso. Basta sostituire name="myForm"con name="vm.myForm"ed in JS, vm.myForm.myText.$$element.focus();.


3

Probabilmente, la soluzione più semplice sull'età ES6.

L'aggiunta seguendo una direttiva di linea rende l'attributo HTML "autofocus" efficace su Angular.js.

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

Ora puoi semplicemente usare la sintassi della messa a fuoco automatica HTML5 come:

<input type="text" autofocus>

@Stephane sì, così diventa.directive('autofocus', ['$timeout', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())})])
Michael Plautz,

2

Solo un principiante qui, ma ero abble per farlo funzionare in un ui.bootstrap.modal con questa direttiva:

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

e nel metodo $ modal.open ho usato il folowing per indicare l'elemento su cui mettere il focus:

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

sul modello ho questo:

<input id="myInputId" focus />

2

La seguente direttiva mi ha aiutato. Utilizzare lo stesso attributo html autofocus per l'input.

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

1
visualizzerai un errore eseguendo questo a meno che non sia incluso jQuery, dovrebbe invece essere element [0] .focus ()
Ryan M

2

Se si utilizza modalInstance e si dispone dell'oggetto, è possibile utilizzare "then" per eseguire azioni dopo aver aperto il modal. Se non si utilizza modalInstance e il codice per aprire il modale è possibile utilizzare l'evento. Il timeout $ non è una buona soluzione.

Puoi fare (Bootstrap3):

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

Su modalInstance puoi consultare la libreria su come eseguire il codice dopo aver aperto modale.

Non utilizzare $ timeout in questo modo, $ timeout può essere 0, 1, 10, 30, 50, 200 o più, questo dipenderà dal computer client e dal processo di apertura modale.

Non usare $ timeout, lascia che il metodo ti dica quando puoi concentrarti;)

Spero che questo aiuto! :)


2

Tutta la risposta precedente non funziona se l'elemento focus desiderato viene iniettato in un modello di direttiva. La seguente direttiva si adatta sia all'elemento semplice sia all'elemento iniettato direttiva (l'ho scritto in dattiloscritto ). accetta il selettore per l'elemento focalizzabile interno. se devi solo focalizzare l'elemento self - non inviare alcun parametro selettore alla direttiva:

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

}

esempio di utilizzo per elemento semplice:

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

esempio di utilizzo per elemento interno (di solito per elemento iniettato dinamico come direttiva con modello):

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

puoi usare qualsiasi selettore jQuery invece di "input"


2

Modifica la direttiva focusMe di Mark Rajcok per lavorare con focus multipli in un elemento.

HTML:

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

nel controller AngularJs:

$scope.myInputFocus= true;

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

1

Voglio contribuire a questa discussione dopo aver cercato una soluzione migliore e non averla trovata, dovendo invece crearla.

Criteri: 1. La soluzione dovrebbe essere indipendente dall'ambito del controllore principale per aumentare la riutilizzabilità. 2. Evitare l'uso di $ watch per monitorare alcune condizioni, entrambe lente, aumentano le dimensioni del ciclo digest e rendono più difficili i test. 3. Evita $ timeout o $ scope. $ Apply () per attivare un ciclo digest. 4. Un elemento di input è presente all'interno dell'elemento in cui la direttiva è utilizzata aperta.

Questa è la soluzione che mi è piaciuta di più:

Direttiva:

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

Spero che questo possa aiutare qualcuno là fuori!


Hai appena ricreato ciò che fa "etichetta" html, potresti semplicemente sostituire "div focus-input" con "etichetta" e sbarazzarti della direttiva.
frst

1

È facile .. prova questo

html

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

javascript

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

È corretto fuori contesto, ma non il modo AngularJS di fare le cose
CodeMonkey

1

Se desideri focalizzare l'attenzione su un particolare elemento, puoi utilizzare l'approccio seguente.

  1. Crea un servizio chiamato 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. Iniettarlo nel controller da dove si desidera chiamare.

  3. Chiama questo servizio.


0

Penso che la direttiva non sia necessaria. Utilizzare gli attributi ID e classe HTML per selezionare l'elemento richiesto e fare in modo che il servizio utilizzi document.getElementById o document.querySelector per applicare lo stato attivo (o equivalenti jQuery).

Markup è standard HTML / direttive angolari con ID / classi aggiunti per la selezione

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

Il controller trasmette un evento

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

Nel servizio UI utilizza querySelector - se ci sono più corrispondenze (diciamo a causa della classe) restituirà solo la prima

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

Potresti voler usare $ timeout () per forzare un ciclo digest


0

Basta buttare un po 'di caffè.

app.directive 'ngAltFocus', ->
    restrict: 'A'
    scope: ngAltFocus: '='
    link: (scope, el, attrs) ->
        scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.