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




example html (25)

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.


Answers

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>

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"


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

Criteri: 1. La soluzione deve essere indipendente dall'ambito del controllore genitore per aumentare la riutilizzabilità. 2. Evita l'uso di $ watch per monitorare alcune condizioni, questo è sia lento, aumenta la dimensione del ciclo digest e rende più difficili i test. 3. Evita $ timeout o $ scope. $ Apply () per attivare un ciclo di digest. 4. Un elemento di input è presente all'interno dell'elemento in cui viene utilizzata la Direttiva 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!


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

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



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.


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

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

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

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

Oppure mettere a fuoco automaticamente quando l'utente completa un campo a lunghezza fissa

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

E ovviamente messa a fuoco dopo il carico

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

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/


Penso che la direttiva non sia necessaria. Utilizzare gli attributi ID e classe HTML per selezionare l'elemento richiesto e richiedere al servizio document.getElementById o document.querySelector di applicare il focus (o gli equivalenti jQuery).

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

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

Controller trasmette eventi

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

Nel servizio di interfaccia utente utilizza querySelector - se ci sono più corrispondenze (ad esempio dovute alla classe) restituirà solo il primo

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

Potresti voler usare $ timeout () per forzare un ciclo di digestione


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

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

Questo sebbene non sia un puro modo angolare di eseguire il compito, tuttavia la sintassi segue lo stile angolare. Jquery interpreta il ruolo indirettamente e direttamente l'accesso al DOM utilizzando Angular (jQLite => JQuery Light).

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


È facile .. prova questo

html

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

javascript

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

Puoi anche utilizzare la funzionalità jqlite integrata in angolare.

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


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

Solo un newbie qui, ma ero in grado di farlo funzionare in un http://angular-ui.github.io/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 in cui mettere a fuoco il focus:

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

sul modello ho questo:

<input id="myInputId" focus />

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


  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.


È anche possibile usare ngModelController . Lavorare con 1.6+ (non 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, è possibile che sia necessario eseguire il wrapping in una funzione di timeout.

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


Primo, un modo ufficiale di concentrarsi è sulla roadmap per 1.1 . Nel frattempo, puoi scrivere una direttiva per implementare la messa a fuoco delle impostazioni.

In secondo luogo, per mettere a fuoco un elemento dopo che è diventato visibile al momento richiede una soluzione alternativa. È sufficiente ritardare la chiamata all'elemento focus () con un $timeout .

Poiché lo stesso controller-modifica-problema DOM esiste per messa a fuoco, sfocatura e selezione, propongo di avere una direttiva 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>

Thread angolare qui: http://goo.gl/ipsx4 , e ulteriori dettagli sono stati scritti qui: http://goo.gl/4rdZa

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


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! :)


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.


È possibile associare il metodo B alla direttiva ng-blur di angular per rilevare quando un input perde il focus

<input type='text' ng-focus='methodA()' ng-blur='methodB()' ng-model='model'>




angularjs angularjs-directive