Stellen Sie den Elementfokus in einem Winkel ein

112

Nachdem ich nach Beispielen gesucht hatte, wie Fokuselemente mit Winkel eingestellt werden, stellte ich fest, dass die meisten von ihnen eine Variable verwenden, um auf den Fokus zu achten, und die meisten verwenden eine andere Variable für jedes Feld, für das sie den Fokus setzen möchten. In einer Form mit vielen Feldern impliziert dies viele verschiedene Variablen.

Mit Blick auf jquery, aber um dies auf eckige Weise zu tun, habe ich eine Lösung gefunden, bei der wir den Fokus in jeder Funktion unter Verwendung der Element-ID setzen. Da ich sehr neu in eckig bin, würde ich gerne einige Meinungen einholen, wenn Dieser Weg ist richtig, habe Probleme, was auch immer, alles, was mir helfen könnte, dies besser im Winkel zu tun.

Grundsätzlich erstelle ich eine Direktive, die einen vom Benutzer mit der Direktive definierten Bereichswert oder das focusElement des Standardwerts überwacht. Wenn dieser Wert mit der ID des Elements übereinstimmt, setzt dieses Element den Fokus selbst.

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

Eine Eingabe, die aus irgendeinem Grund fokussiert werden muss, wird auf diese Weise ausgeführt

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

Hier jedes Element, um den Fokus zu setzen:

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

Und die Beispielfunktion, die den Fokus setzt:

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

Ist das eine gute Lösung im Winkel? Hat es Probleme, die ich aufgrund meiner schlechten Erfahrung noch nicht sehe?

Tiago Zortéa De Conto
quelle

Antworten:

173

Das Problem mit Ihrer Lösung ist, dass sie nicht gut funktioniert, wenn sie an andere Richtlinien gebunden ist, die einen neuen Geltungsbereich schaffen, z ng-repeat. Eine bessere Lösung wäre, einfach eine Servicefunktion zu erstellen, mit der Sie Elemente in Ihren Controllern unbedingt fokussieren oder Elemente deklarativ im HTML-Code fokussieren können.

DEMO

JAVASCRIPT

Bedienung

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

Richtlinie

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

Regler

.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>
Roggenballar
quelle
Diese Lösung gefällt mir sehr gut. Können Sie jedoch den Grund für die Verwendung von $ timeout etwas näher erläutern? Liegt der Grund, warum Sie es verwendet haben, an einem "Angular Thing" oder "DOM Thing"?
user1821052
Es stellt sicher, dass es nach allen Digest-Zyklen ausgeführt wird, die von Angular ausgeführt werden. Dies schließt jedoch Digest-Zyklen aus, die nach einer asynchronen Aktion betroffen sind, die nach dem Timeout ausgeführt wird.
Roggenballar
3
Vielen Dank! Für diejenigen, die sich fragen, wo in den eckigen Dokumenten darauf verwiesen wird, hier der Link (ich habe ewig
gebraucht
@ryeballar, danke!. Schöne einfache Lösung. Nur eine Frage. Kann ich die über Attribut erstellte Factory verwenden, anstatt auf ein Ereignis zu warten?
Pratik Gaikwad
4
Es ist verrückt, wie viel Arbeit im Winkel erforderlich ist, um eine Eingabe zu fokussieren.
Bruno Santos
19

Zu dieser Lösung könnten wir einfach eine Direktive erstellen und sie an das DOM-Element anhängen, das den Fokus erhalten muss, wenn eine bestimmte Bedingung erfüllt ist. Durch diesen Ansatz vermeiden wir die Kopplung des Controllers an DOM-Element-IDs.

Beispielcode-Direktive:

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

Eine mögliche Verwendung

.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">
Braulio
quelle
1
Was passiert, wenn die myConditionVariable $ scope bereits auf true gesetzt wurde und der Benutzer dann auf ein anderes Element fokussiert? Können Sie den Fokus trotzdem erneut auslösen, wenn er myConditionbereits true ist ? Ihr Code überwacht die Änderungen für das Attribut focusOnCondition, wird jedoch nicht ausgelöst, wenn Der Wert, den Sie ändern möchten, ist immer noch der gleiche.
Roggenballar
Ich werde das Beispiel aktualisieren. In unserem Fall haben wir zwei Optionsfelder und wir schalten das Flag je nach Wert auf wahr oder falsch. Sie können einfach das myCondition-Flag auf wahr oder falsch ändern
Braulio
Scheint eine generische Lösung zu sein. Besser als abhängig von IDs. Ich mag das.
Mortb
Falls jemand anderes dies versucht und es nicht funktioniert, musste ich element.focus () ändern; zu Element [0] .focus ();
Adrian Carr
1
Diese Lösung ist viel mehr "eckig" als der oben beschriebene id-basierte Hack.
Setec
11

Ich vermeide DOM-Lookups, Uhren und globale Sender nach Möglichkeit, daher verwende ich einen direkteren Ansatz. Verwenden Sie eine Direktive, um eine einfache Funktion zuzuweisen, die sich auf das Direktivenelement konzentriert. Rufen Sie diese Funktion dann auf, wo immer dies im Rahmen der Steuerung erforderlich ist.

Hier ist ein vereinfachter Ansatz zum Anhängen an den Gültigkeitsbereich. Informationen zum Umgang mit der Controller-as-Syntax finden Sie im vollständigen Snippet.

Richtlinie:

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

und in HTML:

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

oder in der Steuerung:

$scope.focusOnSaveInput();

Bearbeitet , um weitere Erklärungen zum Grund für diesen Ansatz bereitzustellen und das Code-Snippet für die Verwendung als Controller zu erweitern.

Cstricklan
quelle
Das ist sehr schön und hat gut für mich funktioniert. Aber jetzt habe ich eine Reihe von Eingaben ng-repeat, und ich möchte nur die Fokusfunktion für die erste einstellen. Jede Idee , wie ich bedingt eine Fokusfunktion für setzen könnte <input>auf der Grundlage $indexzum Beispiel?
Garret Wilson
Ich bin froh, dass es nützlich ist. Mein Winkel 1 ist ein wenig verrostet, aber Sie sollten in der Lage sein, der Eingabe ein weiteres Attribut hinzuzufügen, z. B. assign-focus-function-if="{{$index===0}}"und dann als erste Zeile der Direktive, bevor Sie eine Funktion zuweisen, wenn dies nicht zutrifft: if (attr.assignFocusFunctionIf===false) return; Hinweis Ich überprüfe, ob Es ist explizit falseund nicht nur falsch, daher funktioniert die Direktive immer noch, wenn dieses Attribut nicht definiert ist.
Cstricklan
Controller-as ist mit lodash viel einfacher. _.set(scope, attributes.focusOnSaveInput, function() { element.focus(); }).
Atomosk
9

Du kannst es versuchen

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

für zB.

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

Es funktioniert für mich.

Anoop
quelle
4
Hinweis: Dies funktioniert nur, wenn die vollständige jQuery verwendet wird, anstatt sich auf in Angular eingebettetes jqLite zu verlassen. Siehe docs.angularjs.org/api/ng/function/angular.element
John Rix
4
Dies ist die jQuery-Methode, keine eckige Methode. In der Frage wird speziell gefragt, wie dies auf eckige Weise geschehen soll.
verzeihen,
4

Eine andere Möglichkeit wäre, Angulars integrierte Pub-Sub-Architektur zu verwenden, um Ihre Direktive zu benachrichtigen, sich zu konzentrieren. Ähnlich wie bei den anderen Ansätzen, aber es ist dann nicht direkt an eine Eigenschaft gebunden, sondern hört stattdessen auf den Bereich für einen bestimmten Schlüssel.

Richtlinie:

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

Regler:

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

Ich habe es vorgezogen, einen Ausdruck zu verwenden. Auf diese Weise kann ich mich beispielsweise auf eine Schaltfläche konzentrieren, wenn ein Feld gültig ist, eine bestimmte Länge erreicht und natürlich nach dem Laden.

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

In einer komplexen Form reduziert dies auch die Notwendigkeit, zusätzliche Bereichsvariablen zum Zwecke der Fokussierung zu erstellen.

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

winry
quelle