web-dev-qa-db-fra.com

Définit le focus de l'élément de la manière angular

Après avoir cherché des exemples de la manière dont les éléments de focus sont définis avec angulaire, j'ai constaté que la plupart d'entre eux utilisaient des variables pour surveiller puis définissaient la focalisation et que la plupart utilisaient une variable différente pour chaque champ sur lequel ils souhaitaient définir la focalisation. Dans un formulaire comportant beaucoup de champs, cela implique de nombreuses variables différentes.

En pensant jquery, mais en voulant le faire de manière angular, j’ai créé une solution que nous avons définie dans toute fonction en utilisant l’identifiant de l’élément. Ainsi, comme je suis très novateur en angulaire, tiens à avoir des opinions si cela est correct, des problèmes, quoi que ce soit, tout ce qui pourrait m'aider à le faire de la meilleure façon qui soit.

Fondamentalement, je crée une directive qui surveille une valeur de portée définie par l'utilisateur avec directive, ou le focusElement par défaut, et lorsque cette valeur est identique à l'id de l'élément, cet élément est lui-même actif.

angular.module('appnamehere')
  .directive('myFocus', function () {
    return {
      restrict: 'A',
      link: function postLink(scope, element, attrs) {
        if (attrs.myFocus == "") {
          attrs.myFocus = "focusElement";
        }
        scope.$watch(attrs.myFocus, function(value) {
          if(value == attrs.id) {
            element[0].focus();
          }
        });
        element.on("blur", function() {
          scope[attrs.myFocus] = "";
          scope.$apply();
        })        
      }
    };
  });

Une entrée qui doit être ciblée par une raison quelconque fera de cette façon

<input my-focus id="input1" type="text" />

Voici n'importe quel élément pour définir le focus:

<a href="" ng-click="clickButton()" >Set focus</a>

Et l'exemple de fonction qui définit le focus:

$scope.clickButton = function() {
    $scope.focusElement = "input1";
}

Est-ce une bonne solution en angulaire? A-t-il des problèmes avec ma mauvaise expérience que je ne vois pas encore?

109

Le problème de votre solution est qu’elle ne fonctionne pas bien lorsqu'elle est liée à d’autres directives qui créent une nouvelle portée, par exemple. ng-repeat. Une meilleure solution consisterait simplement à créer une fonction de service vous permettant de focaliser des éléments de manière impérative dans vos contrôleurs ou de déclarer des éléments de manière déclarative dans le code HTML.

DÉMO

JAVASCRIPT

Service

 .factory('focus', function($timeout, $window) {
    return function(id) {
      // timeout makes sure that it is invoked after any other event has been triggered.
      // e.g. click events that need to run before the focus or
      // inputs elements that are in a disabled state but are enabled when those events
      // are triggered.
      $timeout(function() {
        var element = $window.document.getElementById(id);
        if(element)
          element.focus();
      });
    };
  });

Directive

  .directive('eventFocus', function(focus) {
    return function(scope, elem, attr) {
      elem.on(attr.eventFocus, function() {
        focus(attr.eventFocusId);
      });

      // Removes bound events in the element itself
      // when the scope is destroyed
      scope.$on('$destroy', function() {
        elem.off(attr.eventFocus);
      });
    };
  });

Contrôleur

.controller('Ctrl', function($scope, focus) {
    $scope.doSomething = function() {
      // do something awesome
      focus('email');
    };
  });

HTML

<input type="email" id="email" class="form-control">
<button event-focus="click" event-focus-id="email">Declarative Focus</button>
<button ng-click="doSomething()">Imperative Focus</button>
173
ryeballar

À propos de cette solution, nous pourrions simplement créer une directive et l’attacher à l’élément DOM qui doit avoir le focus lorsqu’une condition donnée est remplie. En suivant cette approche, nous évitons de coupler le contrôleur aux ID d'élément DOM.

Exemple de directive de code:

gbndirectives.directive('focusOnCondition', ['$timeout',
    function ($timeout) {
        var checkDirectivePrerequisites = function (attrs) {
          if (!attrs.focusOnCondition && attrs.focusOnCondition != "") {
                throw "FocusOnCondition missing attribute to evaluate";
          }
        }

        return {            
            restrict: "A",
            link: function (scope, element, attrs, ctrls) {
                checkDirectivePrerequisites(attrs);

                scope.$watch(attrs.focusOnCondition, function (currentValue, lastValue) {
                    if(currentValue == true) {
                        $timeout(function () {                                                
                            element.focus();
                        });
                    }
                });
            }
        };
    }
]);

Une utilisation possible

.controller('Ctrl', function($scope) {
   $scope.myCondition = false;
   // you can just add this to a radiobutton click value
   // or just watch for a value to change...
   $scope.doSomething = function(newMyConditionValue) {
       // do something awesome
       $scope.myCondition = newMyConditionValue;
  };

});

HTML

<input focus-on-condition="myCondition">
18
Braulio

J'aime éviter les recherches dans le DOM, les montres et les émetteurs globaux chaque fois que cela est possible; j'utilise donc une approche plus directe. Utilisez une directive pour affecter une fonction simple qui se concentre sur l'élément de directive. Appelez ensuite cette fonction si nécessaire dans le cadre du contrôleur.

Voici une approche simplifiée pour l'attacher à la portée. Voir l'extrait de code complet pour la gestion de la syntaxe controller-as.

Directif:

app.directive('inputFocusFunction', function () {
    'use strict';
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            scope[attr.inputFocusFunction] = function () {
                element[0].focus();
            };
        }
    };
});

et en html:

<input input-focus-function="focusOnSaveInput" ng-model="saveName">
<button ng-click="focusOnSaveInput()">Focus</button>

ou dans le contrôleur:

$scope.focusOnSaveInput();
angular.module('app', [])
  .directive('inputFocusFunction', function() {
    'use strict';
    return {
      restrict: 'A',
      link: function(scope, element, attr) {
        // Parse the attribute to accomodate assignment to an object
        var parseObj = attr.inputFocusFunction.split('.');
        var attachTo = scope;
        for (var i = 0; i < parseObj.length - 1; i++) {
          attachTo = attachTo[parseObj[i]];
        }
        // assign it to a function that focuses on the decorated element
        attachTo[parseObj[parseObj.length - 1]] = function() {
          element[0].focus();
        };
      }
    };
  })
  .controller('main', function() {});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>

<body ng-app="app" ng-controller="main as vm">
  <input input-focus-function="vm.focusOnSaveInput" ng-model="saveName">
  <button ng-click="vm.focusOnSaveInput()">Focus</button>
</body>

Edited pour fournir plus d'explications sur la raison de cette approche et pour étendre l'extrait de code à l'usage d'un contrôleur.

11
cstricklan

Tu peux essayer

angular.element('#<elementId>').focus();

pour par exemple.

angular.element('#txtUserId').focus();

ça marche pour moi.

9
Anoop

Une autre option serait d'utiliser l'architecture pub-sub intégrée d'Angular afin de notifier votre directive. Semblable aux autres approches, mais il n'est donc pas directement lié à une propriété, mais écoute plutôt la portée d'une clé particulière.

Directif:

angular.module("app").directive("focusOn", function($timeout) {
  return {
    restrict: "A",
    link: function(scope, element, attrs) {
      scope.$on(attrs.focusOn, function(e) {
        $timeout((function() {
          element[0].focus();
        }), 10);
      });
    }
  };
});

HTML:

<input type="text" name="text_input" ng-model="ctrl.model" focus-on="focusTextInput" />

Manette:

//Assume this is within your controller
//And you've hit the point where you want to focus the input:
$scope.$broadcast("focusTextInput");
4
Mattygabe

J'ai préféré utiliser une expression. Cela me permet de faire des choses comme mettre le focus sur un bouton lorsqu'un champ est valide, atteint une certaine longueur et bien sûr après le chargement.

<button type="button" moo-focus-expression="form.phone.$valid">
<button type="submit" moo-focus-expression="smsconfirm.length == 6">
<input type="text" moo-focus-expression="true">

Sur une forme complexe, cela réduit également le besoin de créer des variables de portée supplémentaires à des fins de focalisation.

Voir https://stackoverflow.com/a/29963695/937997

3
winry