Comment mettre l'accent sur le champ de saisie?


759

Quelle est la «méthode angulaire» pour mettre l'accent sur le champ de saisie dans AngularJS?

Exigences plus spécifiques:

  1. Lorsqu'un modal est ouvert, mettez l'accent sur un prédéfini à l' <input>intérieur de ce modal.
  2. À chaque fois que cela <input>devient visible (par exemple en cliquant sur un bouton), mettez l'accent dessus.

J'ai essayé d'atteindre la première exigence avec autofocus, mais cela ne fonctionne que lorsque le Modal est ouvert pour la première fois, et uniquement dans certains navigateurs (par exemple, dans Firefox, cela ne fonctionne pas).

Toute aide serait appréciée.

Réponses:


579
  1. Lorsqu'un modal est ouvert, placez le focus sur une <entrée> prédéfinie à l'intérieur de ce modal.

Définissez une directive et faites-lui $ watch une propriété / déclencheur pour qu'il sache quand concentrer l'élément:

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

Le $ timeout semble être nécessaire pour donner le temps modal au rendu.

«2.» Chaque fois que <input> devient visible (par exemple en cliquant sur un bouton), définissez le focus dessus.

Créez une directive essentiellement comme celle ci-dessus. Observez une propriété de portée et lorsqu'elle devient vraie (définissez-la dans votre gestionnaire ng-click), exécutez element[0].focus(). Selon votre cas d'utilisation, vous pouvez ou non avoir besoin d'un délai d'expiration $ pour celui-ci:

<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


Mise à jour 7/2013 : j'ai vu quelques personnes utiliser mes directives de portée origine et avoir ensuite des problèmes avec les champs de saisie intégrés (c'est-à-dire un champ de saisie dans le modal). Une directive sans nouvelle portée (ou éventuellement une nouvelle portée enfant) devrait atténuer une partie de la douleur. Donc, ci-dessus, j'ai mis à jour la réponse pour ne pas utiliser les étendues isolées. Voici la réponse originale:

Réponse originale pour 1., utilisant une portée isolée:

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 .

Réponse originale pour 2., utilisant une portée isolée:

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

Puisque nous devons réinitialiser la propriété trigger / focusInput dans la directive, '=' est utilisé pour la liaison de données bidirectionnelle. Dans la première directive, «@» était suffisant. Notez également que lorsque vous utilisez '@', nous comparons la valeur de déclenchement à "true" car @ donne toujours une chaîne.


1
Voir aussi la directive "focus" de @ Josh: stackoverflow.com/a/14859639/215945 Il n'a pas utilisé de portée isolée dans son implémentation.
Mark Rajcok

2
@MarkRajcok est juste curieux à ce sujet: cette version fonctionne mais si je mets un ng-modelsur le champ de saisie, la valeur du modèle est perdue lorsque j'utilise cette directive avec la portée isolate utilisée. Le problème ne se produit pas si j'essaie la version de Josh sans la portée isolée. Encore un débutant, et j'aimerais comprendre la différence. Voici un Plunker qui le montre.
Sunil D.

1
J'ai trouvé que # 1 fonctionne très bien avec AngularJS 1.0.6. Cependant, lors de l'exécution sous un débogueur, j'ai remarqué que chaque fois que je rejetais et rouvrais mon modal, je voyais un appel supplémentaire à la fonction qui définit le focus que la fois précédente. J'ai légèrement modifié cette fonction pour délier la montre quand value != "true", et cela a semblé résoudre mon problème.
Andrew Brown

1
Donc ... sur une page avec état, j'ai eu la même chose parce que $ watch est $ watch et les développeurs angulaires aiment $ watch ... eh bien j'ai rencontré un problème, M. @MarkRajcok, un problème que j'ai résolu avec la solution (de base) I proposé ci-dessous.
Ben Lesh

3
hmm pour moi, le code fonctionne, car je peux le voir s’exécuter correctement et l’élément [0] est le bon contrôle. cependant .focus () ne déclenche pas de mise au point. éventuellement bootstrap ou autre chose interfère avec cela?
Sonic Soul

264

(EDIT: j'ai ajouté une solution mise à jour sous cette explication)

Mark Rajcok est l'homme ... et sa réponse est une réponse valide, mais elle a eu un défaut (désolé Mark) ...

... Essayez d'utiliser le booléen pour vous concentrer sur l'entrée, puis brouillez l'entrée, puis essayez de l'utiliser pour recentrer l'entrée. Cela ne fonctionnera que si vous réinitialisez le booléen sur false, puis $ digest, puis réinitialisez-le sur true. Même si vous utilisez une comparaison de chaînes dans votre expression, vous serez obligé de remplacer la chaîne par quelque chose d'autre, $ digest, puis de la modifier à nouveau. (Ce problème a été résolu avec le gestionnaire d'événements flou.)

Je propose donc cette solution alternative:

Utilisez un événement, la fonctionnalité oubliée d'Angular.

JavaScript aime les événements après tout. Les événements sont intrinsèquement mal couplés, et encore mieux, vous évitez d'ajouter une autre $ watch à votre $ digest.

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

Alors maintenant, vous pouvez l'utiliser comme ceci:

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

puis n'importe où dans votre application ...

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

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

C'est génial car vous pouvez faire toutes sortes de choses avec quelque chose comme ça. D'une part, vous pouvez lier des événements qui existent déjà. Pour une autre chose, vous commencez à faire quelque chose d'intelligent en demandant à différentes parties de votre application de publier des événements auxquels d'autres parties de votre application peuvent s'abonner.

Quoi qu'il en soit, ce genre de chose me crie «en fonction des événements». Je pense qu'en tant que développeurs Angular, nous essayons vraiment de marteler les chevilles en forme de $ scope dans des trous de forme d'événement.

Est-ce la meilleure solution? Je ne sais pas. C'est une solution.


Solution mise à jour

Après le commentaire de @ ShimonRachlenko ci-dessous, j'ai légèrement changé ma méthode. Maintenant, j'utilise une combinaison d'un service et d'une directive qui gère un événement "en coulisses":

En dehors de cela, c'est le même principe décrit ci-dessus.

Voici une démo rapide Plunk

Usage

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

La source

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
Vous devez terminer l'appel $broadcastavec $timeoutsi vous souhaitez que cela fonctionne lors de l'entrée dans le contrôleur. Sinon, belle solution.
Shimon Rachlenko du

@ShimonRachlenko - Merci. Mais je ne sais pas ce que vous entendez par $ timeout. Si je voulais diffuser lorsque le constructeur du contrôleur était en cours de traitement, je diffuserais tout de suite. Un délai d'attente ne ferait rien d'autre que de reporter la diffusion à une exécution ultérieure dans la boucle d'événements.
Ben Lesh

1
Oui, et cela suffit pour que la directive s'initialise. Dans le cas contraire, l'événement est diffusé avant que la directive ne commence à l'écouter. Encore une fois, cela n'est nécessaire que lorsque vous souhaitez déclencher votre directive lorsque vous entrez dans la page.
Shimon Rachlenko

2
Tu as raison. Je suppose que je ne l'avais pas utilisé pour me concentrer sur la charge. Je mettrai à jour la réponse avec quelque chose de plus robuste.
Ben Lesh

1
C'est, de loin, la solution la plus élégante et la plus "angulaire". Même si je viens juste de copier le code lorsque j'ai rencontré ce problème pour la première fois, je suis heureux que vous ayez créé un module pour cela! Honnêtement, je pense qu'il vaut la peine d'essayer de le mettre dans le noyau angulaire.
Randolpho

241

J'ai trouvé que certaines des autres réponses étaient trop compliquées alors que tout ce dont vous avez vraiment besoin est

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

l'utilisation est

<input name="theInput" auto-focus>

Nous utilisons le délai d'attente pour laisser les choses dans le dom rendre, même s'il est nul, il attend au moins cela - de cette façon cela fonctionne dans les modaux et ainsi de suite


1
Il y aurait plusieurs façons de le faire, une manière possible qui est vraiment simple et facile serait de définir sur l'oscilloscope (contrôleur ici) l'ID de l'élément sur lequel vous souhaitez vous concentrer lorsque vous cliquez sur le bouton, puis dans le écoutez simplement ceci. Dans ce cas, vous n'auriez pas à placer la directive n'importe où en particulier, juste quelque part dans cette page (j'ai utilisé ce type de directives de surveillance avec succès dans le passé, en particulier pour concentrer les choses et faire défiler les choses) - Alors si vous '' ré utiliser jquery (rendrait cela plus simple) il suffit de trouver cet identifiant d'élément et de le concentrer
ecancil

14
La solution avec zéro timeout ne fonctionne pas pour moi si l'entrée est située dans un modal popup. Mais même 10 ms corrigent le problème
Eugene Fidelin

@ecancil: J'aime votre approche car elle est la plus simple, mais vous devez définir le délai d'attente à ~ 500 ms dans IE car l'animation de l'apparence modale se traduit par un curseur clignotant en dehors de l'entrée. Je ne connais pas une bonne façon de faire en sorte que cela se produise à la fin de l'animation, donc je le force brutalement avec les 500 ms.
Dev93

ne fonctionnera pas, si vous devez extraire plus de données de la base de données, il doit attendre que le contrôleur termine les données d'extraction, alors ajoutez suffisamment de temps à la place de 0
Ali Adravi

1
Pour ceux comme @Ade qui veulent utiliser cela avec un simple ng-click: disons que cliquer sur un bouton a ng-click="showInput = !showInputsur votre entrée. Ensuite, sur votre entrée réelle, ajoutez ng-if="showInput". Le fait de basculer le bouton fera exécuter la directive à chaque fois. J'avais un problème avec cela pendant que j'utilisais ng-showce qui est la mauvaise approche.
JVG

91

HTML a un attribut autofocus.

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

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


29
Malheureusement, cela ne fonctionne qu'une fois, voire pas du tout. Je l'ai utilisé sur une situation de montage angulaire en place , et cela a très bien fonctionné la première fois dans Chrome, pas du tout dans Firefox. Dans Chrome, lorsque je clique sur un autre élément pour le modifier sur place, ou même sur le même élément à nouveau, il ne gagne plus en focus. Les documents indiquent que cela fonctionne au chargement de la page, ce qui ne se produit qu'une seule fois dans une application Angular. Si seulement c'était aussi simple, nous serions tous assis sur une plage gagnant 20%!
Nocturno

62

Vous pouvez également utiliser la fonctionnalité jqlite intégrée à angular.

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


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

29
N'est-ce pas une mauvaise pratique de mettre cela dans un contrôleur?
VitalyB

2
Si mettre cette ligne jqlite dans un contrôleur est une mauvaise pratique, ne serait-il pas préférable de mettre cette ligne jqlite dans une directive?
sports

3
Je reçois ceci:Looking up elements via selectors is not supported by jqLite!
Maria Ines Parnisari

1
@VitalyB J'ai eu un cas où j'avais besoin de brouiller un élément après un appel ajax. Je pourrais ajouter une seule ligne de JS pur dans le rappel à l'intérieur du contrôleur, ou créer une directive entière exclusivement pour le flou de cette entrée. Je pensais juste que c'était trop compliqué, alors j'ai opté pour la première option.
Sebastianb

55

Cela fonctionne bien et une manière angulaire de concentrer le contrôle d'entrée

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

Ce n'est pas une façon angulaire pure de faire la tâche, mais la syntaxe suit le style angulaire. Jquery joue un rôle indirectement et accède directement au DOM en utilisant Angular (jQLite => JQuery Light).

Si nécessaire, ce code peut facilement être placé dans une simple directive angulaire où l'élément est directement accessible.


Je ne suis pas sûr que ce soit une excellente approche pour les applications ViewModel. Dans Angular, cela doit être fait via des directives.
Agat

Par exemple, si quelqu'un change une texbox A et que vous devez afficher une fenêtre contextuelle et mettre l'accent sur une autre zone de texte B.
Sanjeev Singh

1
Looking up elements via selectors is not supported by jqLite!
J'obtiens

6
Autant que je sache, vous devrez d'abord charger jQuery en tant que dépendance, auquel cas il angular.elementdevient un wrapper pour $() / jQuery(). Donc, sans cela, cela ne fonctionnera pas, et vous utilisez simplement jQuery de toute façon (mais corrigez-moi si je me trompe)
JVG

@Jascination: jqLite est développé pour supprimer la dépendance jQuery. Vous n'avez pas besoin de jQuery entier pour accéder à un élément dans DOM. Mais si jQuery est installé, Angular fera référence à jQuery. Vérifiez ceci: docs.angularjs.org/api/angular.element
Sanjeev Singh

31

Je ne pense pas que $ timeout soit un bon moyen de concentrer l'élément sur la création. Voici une méthode utilisant une fonctionnalité angulaire intégrée, creusée dans les profondeurs obscures des documents angulaires. Remarquez comment l'attribut "link" peut être divisé en "pré" et "post", pour les fonctions de pré-lien et de post-lien.

Exemple de travail: 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 />

Documents sur la directive AngularJS complète: https://docs.angularjs.org/api/ng/service/$compile


2
J'ai dû encapsuler l'élément [0] .focus () dans un délai d'attente $ pour que cela fonctionne pour moi.
codin

@bbodenmiller Mon modal a un fondu appliqué, lorsque l'élément est construit, il est invisible (100% transparent), donc le navigateur bloque l'invocation du focus en silence. Apparemment, quelques millisecondes passées permettent de se concentrer sur une entrée / un bouton presque transparent mais visible.
snowindy

1
selon la documentation, les fonctions "lien" sont "postLink" par défaut. voir aussi: bennadel.com/blog/…
jody tate

17

Voici ma solution originale:

plongeur

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

Et le HTML:

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

Ce qu'il fait:

Il concentre l'entrée lorsqu'elle devient visible avec ng-show. Pas d'utilisation de $ watch ou $ ici.


En fait, je crois que {{isVisible}} crée de toute façon une montre, donc la déclaration "No use of $ watch" est incorrecte.
masimplo

Oui, je pense que vous avez raison. Mais cela reste un moyen facile de le faire.
Edhowler

17

J'ai écrit une directive de focus contraignant dans les deux sens, tout comme le modèle récemment.

Vous pouvez utiliser la directive focus comme ceci:

<input focus="someFocusVariable">

Si vous créez une variable d'étendue FocusVariable truen'importe où dans votre contrôleur, l'entrée devient focalisée. Et si vous voulez "brouiller" votre entrée, someFocusVariable peut être défini sur false. C'est comme la première réponse de Mark Rajcok mais avec une liaison bidirectionnelle.

Voici la directive:

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

Usage:

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

Voici le violon:

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


Ce devrait être la bonne réponse. Il couvre tous les cas d'utilisation.
Kunal

11

Pour ceux qui utilisent Angular avec le plugin Bootstrap:

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

Vous pouvez vous accrocher à la openedpromesse de l'instance modale:

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

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

Bien! Cependant dans mon cas, $timeoutavec 50msest dans le besoin au lieu de 0.
lk_vc

8

J'ai trouvé utile d'utiliser une expression générale. De cette façon, vous pouvez faire des choses comme déplacer automatiquement le focus lorsque le texte saisi est valide

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

Ou se concentrer automatiquement lorsque l'utilisateur remplit un champ de longueur fixe

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

Et bien sûr, se concentrer après la charge

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

Le code de la directive:

.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

Ne pas ressusciter un zombie ou brancher ma propre directive (ok c'est exactement ce que je fais):

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

J'utilise maintenant cette directive et elle fonctionne très bien, résout le problème général et n'est pas trop complexe. A renoncé à chercher une solution $ timeout-free. Continuez à trouver des commentaires disant que le focus est "sur la feuille de route" pour Angular 1.1 et 1.2, mais j'utilise 2.x et toujours pas de gestion du focus. Il h.
tekHedd

6

Tout d'abord, une façon officielle de se concentrer est sur la feuille de route pour 1.1 . Pendant ce temps, vous pouvez écrire une directive pour implémenter la définition du focus.

Deuxièmement, pour définir le focus sur un élément après qu'il soit devenu visible, une solution de contournement est actuellement nécessaire. Il suffit de retarder votre appel à focus élément () avec un $timeout.

Parce que le même problème de contrôleur-modifie-DOM existe pour le focus, le flou et la sélection, je propose d'avoir une ng-targetdirective:

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

Fil angulaire ici: http://goo.gl/ipsx4 , et plus de détails blogués ici: http://goo.gl/4rdZa

La directive suivante créera une .focus()fonction à l'intérieur de votre contrôleur comme spécifié par votre ng-targetattribut. (Cela crée un .blur()et un .select()aussi.) Démo: http://jsfiddle.net/bseib/WUcQX/


+1 pour la référence de la feuille de route. En fait, je viens de le voir dans la documentation de 1.2 toujours considérée comme instable ( docs.angularjs.org/api/ng.directive:ngFocus )
Edwin Dalorzo

27
@EdwinDalorzo ngFocussemble être un moyen de gérer les focusévénements, pas un moyen de mettre l'accent sur un élément.
teleclimber

6

Au lieu de créer votre propre directive, il est possible d'utiliser simplement des fonctions javascript pour effectuer un focus.

Voici un exemple.

Dans le fichier html:

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

Dans un fichier javascript, dans un contrôleur par exemple, où vous souhaitez activer le focus:

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

9
Ce n'est pas une «voie angulaire» et ne conseillez pas aux gens de toucher DOM dans leurs contrôleurs.
smajl

Mieux vaut ne pas utiliser vanilla dans Angular, car Angular ne peut pas le suivre ni le synchroniser avec tout le reste.
Edgar Quintero

4

Si vous vouliez juste une mise au point simple qui était contrôlée par un ng-clic.

Html:

<input ut-focus="focusTigger">

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

Directif:

'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 simple qui fonctionne bien avec les modaux:

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

        link: function (scope, element, attrs)
        {


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



        }
    };
}])

Exemple

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

Parfait pour moi. Merci
Alexander

3

Vous pouvez simplement créer une directive qui force le focus sur l'élément décoré sur postLinking:

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

Puis dans votre html:

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

Cela fonctionnerait pour les modaux et les éléments ng-if toggled, pas pour ng-show puisque postLinking ne se produit que sur le traitement HTML.


3

Mark et Blesh ont d'excellentes réponses; cependant, Mark's a un défaut que Blesh souligne (en plus d'être complexe à implémenter), et je pense que la réponse de Blesh a une erreur sémantique dans la création d'un service qui consiste spécifiquement à envoyer une demande de focus au frontend alors que tout ce dont il avait besoin était un moyen de retarder l'événement jusqu'à ce que toutes les directives écoutent.

Voici donc ce que j'ai fini par faire, qui vole beaucoup de la réponse de Blesh mais garde la sémantique de l'événement du contrôleur et le service "après chargement" séparés.

Cela permet à l'événement du contrôleur d'être facilement accroché à d'autres choses que la simple focalisation d'un élément spécifique et permet également d'engager la surcharge de la fonctionnalité "après chargement" uniquement si elle est nécessaire, ce qui peut ne pas être le cas dans de nombreux cas.

Usage

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

  afterLoad(notifyControllerEvent);
});

La source

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

La seule et unique réponse que je (un débutant complet) puisse comprendre. Au lieu de "afterLoad (notifyControllerEvent);", j'avais l'habitude de "notifyControllerEvent ()". Sinon, cela s'est terminé par quelques erreurs.
Vel Murugan S

3

Ceci est également possible à utiliser ngModelController. Travailler avec 1.6+ (ne sais pas avec les anciennes versions).

HTML

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

JS

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

-

NB: Selon le contexte, vous devrez peut-être encapsuler une fonction de temporisation.

NB²: Lors de l'utilisation controllerAs, c'est presque la même chose. Il suffit de remplacer name="myForm"avec name="vm.myForm"et JS, vm.myForm.myText.$$element.focus();.


3

Probablement, la solution la plus simple à l'ère ES6.

L'ajout de la directive liner suivante rend l'attribut HTML «autofocus» efficace sur Angular.js.

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

Maintenant, vous pouvez simplement utiliser la syntaxe de mise au point automatique HTML5 comme:

<input type="text" autofocus>

@Stephane oui, alors ça devient.directive('autofocus', ['$timeout', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())})])
Michael Plautz

2

Juste un débutant ici, mais j'ai pu le faire fonctionner dans un ui.bootstrap.modal avec cette directive:

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

et dans la méthode $ modal.open, j'ai utilisé ce qui suit pour indiquer l'élément où le focus doit être placé:

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

sur le modèle j'ai ceci:

<input id="myInputId" focus />

2

La directive suivante a fait l'affaire pour moi. Utilisez le même attribut html de mise au point automatique pour la saisie.

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

1
vous obtiendrez une erreur en exécutant ceci à moins que jQuery ne soit inclus, il devrait plutôt être l'élément [0] .focus ()
Ryan M

2

Si vous utilisez modalInstance et que vous avez l'objet, vous pouvez utiliser "alors" pour effectuer des actions après l'ouverture du modal. Si vous n'utilisez pas le modalInstance, et codé en dur pour ouvrir le modal, vous pouvez utiliser l'événement. Le délai d'expiration $ n'est pas une bonne solution.

Vous pouvez faire (Bootstrap3):

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

Sur modalInstance, vous pouvez consulter la bibliothèque pour savoir comment exécuter le code après l'ouverture du modal.

N'utilisez pas $ timeout comme celui-ci, le $ timeout peut être 0, 1, 10, 30, 50, 200 ou plus, cela dépendra de l'ordinateur client et du processus pour ouvrir le modal.

N'utilisez pas $ timeout, laissez la méthode vous dire quand vous pouvez vous concentrer;)

J'espère que cette aide! :)


2

Toute la réponse précédente ne fonctionne pas si l'élément de focus souhaité est injecté dans un modèle de directive. La directive suivante s'adapte à la fois à un élément simple ou à un élément injecté de directive (je l'ai écrit en tapuscrit ). il accepte le sélecteur pour l'élément focalisable interne. si vous avez juste besoin de concentrer l'élément self - n'envoyez aucun paramètre de sélection à la directive:

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

}

exemple d'utilisation d'un élément simple:

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

exemple d'utilisation de l'élément interne (généralement pour un élément injecté dynamique comme une directive avec un modèle):

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

vous pouvez utiliser n'importe quel sélecteur jQuery au lieu de "input"


2

J'édite la directive focusMe de Mark Rajcok pour travailler sur plusieurs focus dans un seul élément.

HTML:

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

dans AngularJs Controller:

$scope.myInputFocus= true;

Directive 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

Je veux contribuer à cette discussion après avoir cherché une meilleure solution et ne pas l'avoir trouvée, je dois la créer à la place.

Critères: 1. La solution doit être indépendante de la portée du contrôleur parent pour augmenter la réutilisation. 2. Évitez d'utiliser $ watch pour surveiller certaines conditions, cela est à la fois lent, augmente la taille de la boucle de résumé et rend les tests plus difficiles. 3. Évitez $ timeout ou $ scope. $ Apply () pour déclencher une boucle de résumé. 4. Un élément d'entrée est présent dans l'élément où la directive est utilisée ouverte.

C'est la solution que j'ai le plus appréciée:

Directif:

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

J'espère que cela aidera quelqu'un là-bas!


Vous venez de recréer ce que fait "label" html, vous pouvez simplement remplacer "div focus-input" par "label" et vous débarrasser de la directive.
FRST

1

C'est facile .. essayez ceci

html

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

javascript

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

C'est correct hors contexte, mais pas la façon de faire AngularJS
CodeMonkey

1

Si vous souhaitez mettre l'accent sur un élément particulier, vous pouvez utiliser l'approche ci-dessous.

  1. Créez un service appelé 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. Injectez-le dans le contrôleur d'où vous souhaitez appeler.

  3. Appelez ce service.


0

Je pense que la directive est inutile. Utilisez l'ID HTML et les attributs de classe pour sélectionner l'élément requis et demandez au service d'utiliser document.getElementById ou document.querySelector pour appliquer le focus (ou les équivalents jQuery).

Le balisage est des directives HTML / angulaires standard avec des id / classes ajoutés pour la sélection

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

Le contrôleur diffuse l'événement

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

Dans l'interface utilisateur, le service utilise querySelector - s'il y a plusieurs correspondances (par exemple en raison de la classe), il ne renverra que la première

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

Vous voudrez peut-être utiliser $ timeout () pour forcer un cycle de résumé


0

Je jette juste du café.

app.directive 'ngAltFocus', ->
    restrict: 'A'
    scope: ngAltFocus: '='
    link: (scope, el, attrs) ->
        scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.