angularjs - introduzione - Come impostare l'attenzione sul campo di input?




this angular js (20)

Qual è il 'modo angolare' per focalizzare il campo di input in AngularJS?

Requisiti più specifici:

  1. Quando viene aperta una Modal , imposta lo stato attivo su un <input> predefinito all'interno di questo modal.
  2. Ogni volta che <input> diventa visibile (ad es. Facendo clic su un pulsante), imposta lo stato attivo su di esso.

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

Qualsiasi aiuto sarà apprezzato.


  1. Quando viene aperta una modale, imposta lo stato attivo su un <input> predefinito all'interno di questo modal.

Definisci una direttiva e chiedi a $ 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 fornire il tempo modale per il rendering.

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

Crea una direttiva essenzialmente simile a quella sopra. Osserva alcune proprietà dell'oscilloscopio e quando diventa true (impostalo nel gestore del tuo ng-click), esegui l' element[0].focus() . A seconda del tuo caso d'uso, potresti avere o meno bisogno di un timeout $ 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 utilizzare le mie direttive originali di ambito isolato e quindi avere problemi con i campi di input incorporati (cioè, un campo di input nel modale). Una direttiva senza un nuovo ambito (o forse un nuovo ambito figlio) dovrebbe alleviare parte del dolore. Così sopra ho aggiornato la risposta per non utilizzare 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., utilizzando 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 l'associazione dati 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.


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

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

... Prova a utilizzare il valore booleano per mettere a fuoco l'input, quindi sfocare l'input, quindi provare a utilizzarlo per mettere nuovamente a fuoco l'input. Non funzionerà se non resetti il ​​valore booleano su false, quindi $ digest, quindi lo ripristini su true. Anche se utilizzi un confronto tra stringhe nella tua espressione, sarai obbligato a cambiare la stringa in qualcos'altro, $ digest, quindi cambiarla di nuovo. (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 dopotutto. Gli eventi sono intrinsecamente associati, e ancora meglio, si evita di aggiungere un altro $ watch al proprio $ digest.

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

Quindi ora puoi 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 ogni sorta di cose con qualcosa di simile. Per uno, potresti legarti ad eventi che già esistono. Per un'altra cosa, si inizia a fare qualcosa di intelligente avendo parti diverse della tua app pubblicare eventi a cui altre parti della tua app possono iscriversi.

Comunque, questo tipo di cose urla "evento guidato" per me. Penso che, in qualità di sviluppatori angolari, proviamo davvero a martellare i pioli a forma di scope $ nei buchi delle forme degli eventi.

È la soluzione migliore? Non so. È una soluzione

Soluzione aggiornata

Dopo il commento di ShimonRachlenko qui sotto, ho cambiato il mio modo di farlo leggermente. Ora uso una combinazione di un servizio e una direttiva che gestiscono un evento "dietro le quinte":

Oltre a questo, è lo stesso principio descritto sopra.

Ecco una breve demo 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);
    });
  }
});

È facile .. prova questo

html

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

javascript

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

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'input in quanto diventa visibile con ng-show. Nessun uso di $ watch o $ qui.


Ho scritto una direttiva focus binding bidirezionale, proprio come il modello di recente.

Puoi usare la direttiva focus in questo modo:

<input focus="someFocusVariable">

Se si imposta una variabile dell'oscilloscopio FocusVariable in qualsiasi punto del controller, l'input viene focalizzato. E se vuoi "sfocare" il tuo input allora, someFocusVariable può essere impostato su false. È come la prima risposta di Mark Rajcok, ma con rilegatura a doppio senso.

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/


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'uso è

<input name="theInput" auto-focus>

Usiamo il timeout per lasciare che le cose nel rendering dom, anche se è zero, almeno lo aspetta - in questo modo funziona nei modali e anche in quello


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

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

La seguente direttiva ha fatto il trucco per me. Usa lo stesso attributo html di autofocus per l'input.

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

Modifico la direttiva focusMe di "Mark Rajcok" per lavorare su più focus 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);
                }
            });
        }
    };
});

Non credo che $ timeout sia un buon modo per focalizzare l'elemento sulla creazione. Ecco un metodo che utilizza la funzionalità angolare incorporata, scavata nelle profondità oscure dei documenti angolari. Si noti come l'attributo "link" 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


Per coloro che usano Angular con il plugin Bootstrap:

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

Puoi collegare alla promessa opened dell'istanza modale:

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

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

Potresti semplicemente creare una direttiva che costringa a focalizzare l'attenzione sull'elemento decorato sul 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"/>

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


Probabilmente, la soluzione più semplice dell'era ES6.

L'aggiunta della seguente direttiva di liner rende l'attributo HTML "autofocus" efficace su Angular.js.

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

Ora puoi utilizzare la sintassi dell'autofocus HTML5 come:

<input type="text" autofocus>

Puoi anche utilizzare la funzionalità jqlite integrata in angolare.

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


Se si desidera semplicemente una messa a fuoco semplice controllata da un ng-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();
                });
            });
        }
    }
});

Se si utilizza modalInstance e si dispone dell'oggetto, è possibile utilizzare "then" per eseguire azioni dopo aver aperto la modal. Se non si utilizza il modalInstance e il codice è rigido per aprire il modal, è 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 guardare la libreria su come eseguire il codice dopo la modal aperta.

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

Non usare $ timeout lascia che sia il metodo a dirti quando puoi concentrarti;)

Spero che questo aiuto! :)


Tutte le risposte precedenti non funzionano se l'elemento di messa a fuoco desiderato viene iniettato in un modello di direttiva. La seguente direttiva si adatta sia all'elemento iniettato semplice sia all'elemento di direttiva (l'ho scritto in dattiloscritto ). accetta selettore per elemento focalizzabile interno. se hai solo bisogno di focalizzare l'elemento self - non inviare alcun parametro selector 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 dinamico iniettato come direttiva con modello):

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

puoi usare qualsiasi selettore jQuery invece di "input"


Uno semplice che funziona bene con le 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 />

Non è sicuro se affidarsi al timeout sia una buona idea, ma questo funziona per ng-repeat perché questo codice viene eseguito DOPO che angularjs aggiorna il DOM, quindi assicurati che tutti gli oggetti siano presenti:

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

Sto solo buttando un po 'di caffè.

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




angularjs-directive