Ustaw ostrość elementu pod kątem

113

Po wyszukaniu przykładów tego, jak elementy ustawiania ostrości za pomocą kątów, zauważyłem, że większość z nich używa jakiejś zmiennej do obserwowania, a następnie ustawia ostrość, a większość z nich używa jednej zmiennej dla każdego pola, na które chce ustawić ostrość. W formie z wieloma polami oznacza to wiele różnych zmiennych.

Mając na uwadze jquery, ale chcąc to zrobić w sposób kątowy, stworzyłem rozwiązanie, w którym ustawiamy fokus w dowolnej funkcji za pomocą id elementu, więc ponieważ jestem bardzo nowy w angularach, chciałbym uzyskać opinie, jeśli ten sposób jest słuszny, mam problemy, cokolwiek, cokolwiek, co mogłoby mi pomóc w lepszym rozwiązaniu w kątowym.

Zasadniczo tworzę dyrektywę, która obserwuje wartość zakresu zdefiniowaną przez użytkownika za pomocą dyrektywy lub domyślny focusElement, a gdy ta wartość jest taka sama jak id elementu, ten element ustawia ostrość.

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

Wkład, na którym z jakiegoś powodu musi się skupić, zrobi to w ten sposób

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

Tutaj dowolny element do ustawienia ostrości:

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

I przykładowa funkcja ustawiająca fokus:

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

Czy to dobre rozwiązanie w kątowym? Czy ma problemy, których z moim słabym doświadczeniem jeszcze nie widzę?

Tiago Zortéa De Conto
źródło

Odpowiedzi:

173

Problem z rozwiązaniem polega na tym, że nie działa ono dobrze, gdy jest powiązane z innymi dyrektywami, które tworzą nowy zakres, np ng-repeat. Lepszym rozwiązaniem byłoby po prostu utworzenie funkcji usługi, która umożliwia imperatywne skupienie elementów w kontrolerach lub deklaratywne skupienie elementów w kodzie HTML.

PRÓBNY

JAVASCRIPT

Usługa

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

Dyrektywa

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

Kontroler

.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>
ryeballar
źródło
Bardzo podoba mi się to rozwiązanie. Czy możesz jednak wyjaśnić, dlaczego nieco częściej używasz $ timeout? Czy powodem, dla którego go użyłeś, jest „Angular Thing” lub „DOM Thing”?
user1821052
Zapewnia, że ​​działa po wszystkich cyklach podsumowania, które robi angular, ale wyklucza to cykle podsumowania, na które ma wpływ akcja asynchroniczna wykonywana po przekroczeniu limitu czasu.
ryeballar
3
Dzięki! Dla tych, którzy zastanawiają się, gdzie to jest wymienione w dokumentach kątowych, oto link (znalezienie go zajęło mi wieczność)
user1821052
@ryeballar, Thanks !. Ładne proste rozwiązanie. Tylko pytanie. Czy mogę użyć fabryki utworzonej za pomocą atrybutu zamiast czekać na jakieś zdarzenie?
Pratik Gaikwad
4
To szalone, ile pracy jest konieczne w kątowym, aby skupić dane wejściowe.
Bruno Santos
19

Jeśli chodzi o to rozwiązanie, moglibyśmy po prostu stworzyć dyrektywę i dołączyć ją do elementu DOM, który ma być aktywny po spełnieniu danego warunku. Postępując zgodnie z tym podejściem, unikamy łączenia kontrolera z identyfikatorami elementów DOM.

Przykładowa dyrektywa w kodzie:

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

Możliwe użycie

.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
źródło
1
co się stanie, gdy myConditionzmienna $ scope została już ustawiona na true, a następnie użytkownik wybierze skupienie się na innym elemencie, czy nadal możesz ponownie aktywować fokus, gdy myConditionjest już prawdziwy, twój kod obserwuje zmiany atrybutu, focusOnConditionale nie zostanie wyzwolony, gdy wartość, którą próbujesz zmienić, jest nadal taka sama.
ryeballar
Mam zamiar zaktualizować próbkę, w naszym przypadku mamy dwa przyciski opcji i przełączamy flagę na true lub false w zależności od wartości, możesz po prostu zmienić flagę myCondition na true lub false
Braulio
Wydaje się, że to ogólne rozwiązanie. Lepiej niż w zależności od identyfikatorów. Lubię to.
Mortb
W przypadku, gdyby ktokolwiek inny spróbował tego i nie zadziałało, musiałem zmienić element.focus (); do elementu [0] .focus ();
Adrian Carr,
1
To rozwiązanie jest znacznie bardziej „kanciaste” niż powyższy hack oparty na identyfikatorze.
setec
11

Lubię unikać wyszukiwań DOM, zegarków i globalnych emiterów, gdy tylko jest to możliwe, więc używam bardziej bezpośredniego podejścia. Użyj dyrektywy, aby przypisać prostą funkcję, która koncentruje się na elemencie dyrektywy. Następnie wywołaj tę funkcję w dowolnym miejscu w zakresie kontrolera.

Oto uproszczone podejście do dołączania go do zakresu. Zobacz pełny fragment opisujący składnię kontrolera jako.

Dyrektywa:

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

oraz w html:

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

lub w kontrolerze:

$scope.focusOnSaveInput();

Edytowano, aby zapewnić więcej wyjaśnień na temat powodu tego podejścia i rozszerzyć fragment kodu dla kontrolera jako użycia.

cstricklan
źródło
To bardzo miłe i dobrze mi się sprawdza. Ale teraz mam zestaw danych wejściowych używających ng-repeati chcę ustawić funkcję fokusu tylko dla pierwszego. Masz pomysł, jak mógłbym warunkowo ustawić funkcję fokusu na <input>podstawie na $indexprzykład?
Garret Wilson
Cieszę się, że to przydatne. Mój kąt 1 jest trochę zardzewiały, ale powinieneś być w stanie dodać kolejny atrybut do wejścia, na przykład assign-focus-function-if="{{$index===0}}", a następnie jako pierwsza linia dyrektywy wyjdź wcześnie przed przypisaniem funkcji, jeśli to nie jest prawda: if (attr.assignFocusFunctionIf===false) return; Uwaga, sprawdzam, czy jest wyraźnie, falsea nie tylko falsey, więc dyrektywa będzie nadal działać, jeśli ten atrybut nie zostanie zdefiniowany.
cstricklan
Kontroler - jak jest znacznie prostszy w przypadku lodash. _.set(scope, attributes.focusOnSaveInput, function() { element.focus(); }).
Atomosk,
9

Możesz spróbować

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

np.

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

to działa dla mnie.

Anoop
źródło
4
Uwaga: działałoby to tylko w przypadku korzystania z pełnego jQuery zamiast polegania na jqLite osadzonym w Angular. Zobacz docs.angularjs.org/api/ng/function/angular.element
John Rix
4
To jest sposób robienia tego w jQuery, a nie kątowy. Pytanie dotyczy konkretnie tego, jak to zrobić w sposób kątowy.
wybaczenie
4

Inną opcją byłoby użycie wbudowanej architektury pub-sub Angulara w celu powiadomienia dyrektywy o skupieniu. Podobne do innych podejść, ale nie jest wtedy bezpośrednio powiązane z właściwością, a zamiast tego nasłuchuje w swoim zakresie określonego klucza.

Dyrektywa:

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

Kontroler:

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

Wolałem użyć wyrażenia. To pozwala mi robić takie rzeczy, jak skupienie się na przycisku, gdy pole jest prawidłowe, osiąga określoną długość i oczywiście po załadowaniu.

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

W złożonej formie ogranicza to również potrzebę tworzenia dodatkowych zmiennych zakresu w celu skupienia uwagi.

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

winiarnia
źródło