Jak ustawić fokus na polu wprowadzania?

759

Jaki jest „Angularowy sposób”, aby skupić się na polu wejściowym w AngularJS?

Bardziej szczegółowe wymagania:

  1. Po otwarciu modalu ustaw fokus na predefiniowanym <input>w tym modalu.
  2. Za każdym razem, gdy <input>staje się widoczny (np. Klikając jakiś przycisk), ustaw na nim fokus.

Starałem się osiągnąć pierwszy wymóg z autofocus, ale to działa tylko wtedy, gdy Modal jest otwarty po raz pierwszy, i tylko w niektórych przeglądarkach (np Firefox nie działa).

Każda pomoc będzie mile widziana.

Misza Moroszko
źródło

Odpowiedzi:

579
  1. Kiedy modal jest otwarty, ustaw fokus na predefiniowanym <wejściu> wewnątrz tego modalu.

Zdefiniuj dyrektywę i poproś ją o obejrzenie właściwości / wyzwalacza, aby wiedział, kiedy skupić element:

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

Limit czasu $ wydaje się potrzebny, aby dać czas modalny na renderowanie.

„2.” Za każdym razem, gdy <wejście> staje się widoczne (np. Klikając jakiś przycisk), ustaw na nim fokus.

Utwórz dyrektywę zasadniczo podobną do powyższej. Obserwuj jakąś właściwość zakresu, a gdy stanie się prawdą (ustaw ją w module obsługi ng-click), uruchom element[0].focus(). W zależności od przypadku użycia może być potrzebny limit czasu dla tego:

<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


Aktualizacja 7/2013 : widziałem, jak kilka osób korzysta z moich oryginalnych dyrektyw zakresu izolowania, a następnie mają problemy z osadzonymi polami wejściowymi (tj. Polem wejściowym w module). Dyrektywa bez nowego zakresu (lub ewentualnie nowego zakresu potomnego) powinna złagodzić część bólu. Powyżej zaktualizowałem odpowiedź, aby nie używać zakresów izolowanych. Poniżej znajduje się oryginalna odpowiedź:

Oryginalna odpowiedź na 1., przy użyciu zakresu izolowania:

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 .

Oryginalna odpowiedź na 2., przy użyciu zakresu izolowania:

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

Ponieważ musimy zresetować właściwość trigger / focusInput w dyrektywie, „=” jest używane do dwukierunkowego wiązania danych. W pierwszej dyrektywie wystarczyło „@”. Zauważ też, że używając „@” porównujemy wartość wyzwalacza z „true”, ponieważ @ zawsze daje ciąg znaków.

Mark Rajcok
źródło
1
Zobacz także dyrektywę „focus” @ Josha: stackoverflow.com/a/14859639/215945 W swojej implementacji nie używał zakresu izolowania.
Mark Rajcok
2
@ MarkRajcok po prostu mnie ciekawi: ta wersja działa, ale jeśli ustawię wartość ng-modelw polu wejściowym, wartość modelu zostanie utracona, gdy użyję tej dyrektywy z użyciem zakresu izolowania. Problem nie występuje, jeśli wypróbuję wersję Josha bez zakresu izolowania. Nadal jestem nowicjuszem i chciałbym zrozumieć różnicę. Oto Plunker, który to pokazuje.
Sunil D.
1
Odkryłem, że # 1 działa bardzo dobrze z AngularJS 1.0.6. Jednak podczas pracy z debuggerem zauważyłem, że za każdym razem, gdy zwalniałem i ponownie otwierałem mój modal, widziałem jeszcze jedno dodatkowe wywołanie funkcji, która ustawia ostrość, niż wcześniej. Lekko zmodyfikowałem tę funkcję, aby cofnąć powiązanie zegarka, kiedy to value != "true", jak się wydaje, rozwiązało mój problem.
Andrew Brown
1
Więc ... na stanowczej stronie miałem to samo, ponieważ $ watch to $ watch, a programiści kątowi uwielbiają $ watch ... cóż, natrafiłem na problem, panie @MarkRajcok, problem, który rozwiązałem za pomocą (podstawowego) rozwiązania I zaproponowane poniżej.
Ben Lesh,
3
hmm dla mnie kod działa, ponieważ widzę, że działa poprawnie, a element [0] jest właściwą kontrolą. jednak .focus () nie wyzwala ostrości. być może bootstrap lub coś innego przeszkadza w tym?
Sonic Soul,
264

(EDYCJA: Dodałem zaktualizowane rozwiązanie poniżej tego wyjaśnienia)

Mark Rajcok jest mężczyzną ... i jego odpowiedź jest poprawną odpowiedzią, ale ma wadę (przepraszam Mark) ...

... Spróbuj użyć wartości logicznej, aby ustawić ostrość na wejściu, a następnie rozmyj wejście, a następnie spróbuj użyć go, aby ponownie ustawić ostrość. Nie będzie działać, chyba że zresetujesz wartość logiczną na false, a następnie $ digest, a następnie zresetujesz ją z powrotem na true. Nawet jeśli użyjesz porównania ciągu w swoim wyrażeniu, będziesz zmuszony zmienić ciąg na coś innego, $ digest, a następnie zmień go z powrotem. (Rozwiązano ten problem za pomocą procedury obsługi zdarzenia rozmycia).

Proponuję więc to alternatywne rozwiązanie:

Użyj zdarzenia, zapomnianej funkcji Angulara.

JavaScript w końcu uwielbia wydarzenia. Wydarzenia są z natury luźno powiązane, a co więcej, unikaj dodawania kolejnego $ watch do $ streszczenia.

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

Więc teraz możesz użyć tego w ten sposób:

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

a następnie w dowolnym miejscu w Twojej aplikacji ...

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

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

To jest niesamowite, ponieważ możesz robić różne rzeczy za pomocą czegoś takiego. Po pierwsze, możesz powiązać się z wydarzeniami, które już istnieją. Po drugie, zacznij robić coś inteligentnego, publikując zdarzenia w różnych częściach aplikacji, które mogą subskrybować inne części aplikacji.

W każdym razie ten rodzaj rzeczy krzyczy do mnie „zdarzenie napędzane”. Myślę, że jako programiści Angulara bardzo ciężko próbujemy wbić kołki w kształcie lunety w otwory o kształcie zdarzenia.

Czy to najlepsze rozwiązanie? Nie wiem. To rozwiązanie.


Zaktualizowane rozwiązanie

Po komentarzu @ ShimonRachlenko poniżej zmieniłem nieco moją metodę robienia tego. Teraz używam kombinacji usługi i dyrektywy, która obsługuje wydarzenie „za kulisami”:

Poza tym jest to ta sama zasada opisana powyżej.

Oto szybkie demo Plunk

Stosowanie

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

Źródło

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);
    });
  }
});
Ben Lesh
źródło
3
Trzeba zawinąć wywołanie $broadcastze $timeoutjeśli chcesz to do pracy na wejściu sterownika. W przeciwnym razie fajne rozwiązanie.
Shimon Rachlenko,
@ShimonRachlenko - Dzięki. Ale nie jestem pewien, co masz na myśli przez limit czasu $. Gdybym chciał nadawać, gdy konstruktor kontrolera był przetwarzany, po prostu nadawałbym właśnie wtedy. Limit czasu nie zrobiłby nic poza odroczeniem transmisji do późniejszego wykonania w pętli zdarzeń.
Ben Lesh
1
Tak, i to wystarczy do zainicjowania dyrektywy. Poza tym zdarzenie jest transmitowane, zanim dyrektywa zacznie go słuchać. Ponownie, jest to potrzebne tylko wtedy, gdy chcesz uruchomić dyrektywę po wejściu na stronę.
Shimon Rachlenko,
2
Masz rację. Chyba nie użyłem go do skupienia się na obciążeniu. Zaktualizuję odpowiedź za pomocą czegoś bardziej niezawodnego.
Ben Lesh,
1
Jest to zdecydowanie najbardziej eleganckie rozwiązanie w „kanciasty sposób”. Chociaż w większości po prostu skopiowałem kod, gdy po raz pierwszy napotkałem ten problem, cieszę się, że stworzyłeś dla niego moduł! Naprawdę uważam, że warto spróbować wprowadzić go do kąta rdzenia.
Randolpho
241

Odkryłem, że niektóre inne odpowiedzi są zbyt skomplikowane, gdy naprawdę potrzebujesz tylko tego

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

użycie jest

<input name="theInput" auto-focus>

Używamy limitu czasu, aby pozwolić renderować rzeczy w dom, nawet jeśli jest to zero, to przynajmniej na to czeka - w ten sposób działa to w modach i tak dalej

ecancil
źródło
1
Można to zrobić na kilka sposobów, jednym z możliwych sposobów, który jest naprawdę prosty i prosty, jest ustawienie zakresu (tutaj kontrolera) ustawienia identyfikatora elementu, na którym chcesz się skupić po kliknięciu przycisku, a następnie w dyrektywa po prostu tego słucha. W tym przypadku nie musisz umieszczać tej dyrektywy w żadnym miejscu, tylko gdzieś na tej stronie (w przeszłości z powodzeniem korzystałem z tego rodzaju dyrektyw obserwatora, szczególnie w zakresie ustawiania ostrości i przewijania) - to jeśli „ ponownie używam jquery (uprościłoby to) po prostu znajdź ten identyfikator elementu i skoncentruj go
ecancil
14
Rozwiązanie z zerowym limitem czasu nie działa dla mnie, jeśli dane wejściowe znajdują się w wyskakującym module. Ale nawet 10 ms naprawia problem
Eugene Fidelin
@ecancil: Podoba mi się twoje podejście, ponieważ jest najprostsze, ale musisz ustawić limit czasu na ~ 500ms w IE, ponieważ animacja modalnego wyglądu powoduje miganie kursora poza wejściem. Nie wiem, jak to zrobić, gdy kończy się animacja, więc brutalnie wymusiłem to z 500ms.
Dev93,
nie będzie działać, jeśli musisz pobrać więcej danych z bazy danych, musisz poczekać, aż kontroler zakończy pobieranie danych, więc dodaj wystarczająco dużo czasu zamiast 0
Ali Adravi
1
Dla tych jak @Ade, którzy chcą użyć tego w prosty sposób ng-click: powiedzmy, że kliknięcie przycisku ma ng-click="showInput = !showInputna wejściu. Następnie na podstawie rzeczywistych danych wejściowych dodaj ng-if="showInput". Przełączenie przycisku spowoduje ponowne uruchomienie dyrektywy za każdym razem. Miałem z tym problem, ponieważ ng-showużywałem niewłaściwego podejścia.
JVG
91

HTML ma atrybut autofocus.

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

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

Rayron Victor
źródło
29
Niestety działa to tylko raz, jeśli w ogóle. Użyłem tego w sytuacji edycji Angular na miejscu i zadziałało to świetnie po raz pierwszy w Chrome, wcale nie w Firefoksie. W Chrome, kiedy klikam inny element, aby go edytować, lub nawet ponownie na tym samym elemencie, przestaje on być aktywny. Dokumenty stwierdzają, że działa przy ładowaniu strony, co dzieje się tylko raz w aplikacji Angular. Gdyby to było takie proste, wszyscy byśmy siedzieli na plaży i zarabiali 20%!
Nocturno,
62

Możesz także użyć funkcji jqlite wbudowanej w angular.

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

JordanC
źródło
2
Bez załadowanej jquery: angular.forEach (document.querySelectorAll ('. Selector'), funkcja (elem) {elem.focus ();});
Dan Benamy
29
Czy nie jest złą praktyką umieszczanie tego w kontrolerze?
VitalyB
2
Jeśli umieszczenie tej linii jqlite w kontrolerze jest złą praktyką, czy nie lepiej byłoby umieścić tę linię jqlite w dyrektywie?
sport
3
Rozumiem:Looking up elements via selectors is not supported by jqLite!
Maria Ines Parnisari
1
@VitalyB Miałem przypadek, w którym musiałem rozmazać element po wywołaniu ajax. Mógłbym dodać tylko jedną linię czystego JS do wywołania zwrotnego w kontrolerze lub zbudować całą dyrektywę wyłącznie dla zatarcia tego wejścia. Po prostu czułem, że to było zbyt kłopotliwe, więc wybrałem pierwszą opcję.
Sebastianb,
55

Działa to dobrze i pozwala na skupienie kontroli wejścia

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

Nie jest to jednak czysto kątowy sposób wykonywania zadania, ale składnia jest zgodna ze stylem kątowym. Jquery odgrywa rolę pośrednio i bezpośrednio uzyskuje dostęp do DOM za pomocą Angulara (jQLite => JQuery Light).

W razie potrzeby kod ten można łatwo umieścić w prostej dyrektywie kątowej, w której element jest bezpośrednio dostępny.

Sanjeev Singh
źródło
Nie jestem pewien, czy to świetne podejście do aplikacji ViewModel. W Angular należy to zrobić za pomocą dyrektyw.
Agat
Np. jeśli ktoś zmieni pole tekstowe A i musisz pokazać wyskakujące okienko i ustawić fokus na innym polu tekstowym B.
Sanjeev Singh
1
Podczas używania tego pojawia się błąd:Looking up elements via selectors is not supported by jqLite!
JVG
6
O ile mogę powiedzieć, musisz najpierw załadować jQuery jako zależność, w którym to przypadku angular.elementstaje się to opakowanie $() / jQuery(). Bez tego to nie zadziała, a i tak po prostu używasz jQuery (ale popraw mnie, jeśli się mylę)
JVG
@Jascination: jqLite został opracowany w celu usunięcia zależności jQuery. Nie potrzebujesz całego jQuery, aby uzyskać dostęp do elementu w DOM. Ale jeśli masz jQuery zainstalowany, to Angular poleci jQuery. Sprawdź to: docs.quarejs.org/api/angular.element
Sanjeev Singh
31

Nie sądzę, aby limit czasu $ był dobrym sposobem na skoncentrowanie elementu na tworzeniu. Oto metoda wykorzystująca wbudowaną funkcjonalność kątową, wykopaną z mrocznych głębi dokumentów kątowych. Zwróć uwagę, w jaki sposób atrybut „link” można podzielić na „przed” i „po”, dla funkcji przed i po linku.

Przykład roboczy: 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 />

Pełna dokumentacja dyrektywy AngularJS: https://docs.angularjs.org/api/ng/service/$compile

Cody Moniz
źródło
2
Musiałem zawinąć element [0] .focus () w limit czasu $, aby działał dla mnie.
kodina
@bbodenmiller Mój modal ma zastosowaną funkcję wyciszania, gdy element jest konstruowany, jest niewidoczny (100% przezroczysty), więc bloki przeglądarki skupiają się na wywołaniu w ciszy. Najwyraźniej kilka milisekund minęło, pozwalając skupić się na prawie przezroczystym, ale widocznym wejściu / przycisku.
snowindy
1
zgodnie z dokumentami funkcje „link” są domyślnie „postLink”. zobacz także: bennadel.com/blog/…
jody tate
17

Oto moje oryginalne rozwiązanie:

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

I HTML:

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

Co to robi:

Skupia wejście, gdy staje się widoczne w ng-show. Nie ma tu możliwości korzystania z $ watch ani $.

Edhowler
źródło
Właściwie uważam, że {{isVisible}} i tak tworzy zegarek, więc instrukcja „No use of $ watch” jest niepoprawna.
masimplo
Tak, myślę, że masz rację. Ale nadal służy jako łatwy sposób na zrobienie tego.
Edhowler
17

Napisałem dwuznacznie wiążącą dyrektywę fokusową, podobnie jak ostatnio model.

Możesz użyć dyrektywy fokusowej w następujący sposób:

<input focus="someFocusVariable">

Jeśli wprowadzisz zmienną zakresu FocusVariable true w dowolnym miejscu kontrolera, dane wejściowe zostaną zogniskowane. A jeśli chcesz „rozmazać” dane wejściowe, someFocusVariable można ustawić na false. To jak pierwsza odpowiedź Marka Rajcoka, ale z dwukierunkowym wiązaniem.

Oto dyrektywa:

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

Stosowanie:

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

Oto skrzypce:

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

Umut Benzer
źródło
To powinna być poprawna odpowiedź. Obejmuje wszystkie przypadki użycia.
Kunal,
11

Dla tych, którzy używają Angulara z wtyczką Bootstrap:

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

Możesz sprawdzić openedobietnicę wystąpienia modalnego:

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

modalInstance.result.then(function ( etc...
BPH
źródło
Dobrze! Jednak w moim przypadku $timeoutz 50msjest w potrzebie zamiast 0.
lk_vc,
8

Uznałem, że użyteczne jest użycie ogólnego wyrażenia. W ten sposób możesz robić rzeczy takie jak automatyczne przesuwanie fokusa, gdy wprowadzony tekst jest prawidłowy

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

Lub automatycznie ustaw ostrość, gdy użytkownik wypełni pole o stałej długości

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

I oczywiście ostrość po załadowaniu

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

Kod dyrektywy:

.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
                        }
                    }
                });
            }
        }
    };
});
winiarnia
źródło
7

Nie wskrzeszać zombie ani nie stosować własnej dyrektywy (ok, właśnie to robię):

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
        };
    }
})();
Jon Hieb
źródło
Korzystam teraz z tej dyrektywy i działa ona świetnie, rozwiązuje ogólny problem i nie jest zbyt skomplikowana. Porzuciłem szukanie rozwiązania bez limitu czasu. Znajduj komentarze, które mówią, że fokus jest „na mapie drogowej” dla Angulara 1.1 i 1.2, ale używam 2.x i nadal nie mam zarządzania fokusami. Heh
tekHedd,
6

Po pierwsze, oficjalnym sposobem skoncentrowania się jest na mapie drogowej dla wersji 1.1 . W międzyczasie możesz napisać dyrektywę, aby zaimplementować fokus ustawień.

Po drugie, aby ustawić fokus na elemencie, który stanie się widoczny, obecnie wymaga obejścia. Po prostu opóźnij wywołanie elementu focus () za pomocą $timeout.

Ponieważ ten sam problem z kontrolerem-modyfikującym-DOM istnieje dla skupienia, rozmycia i wyboru, proponuję mieć ng-targetdyrektywę:

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

Kątowy wątek tutaj: http://goo.gl/ipsx4 , a więcej szczegółów blogowanych tutaj: http://goo.gl/4rdZa

Poniższa dyrektywa utworzy .focus()funkcję w twoim kontrolerze zgodnie z twoim ng-targetatrybutem. (Tworzy również .blur()a .select().) Demo: http://jsfiddle.net/bseib/WUcQX/

broc.seib
źródło
+1 za odniesienie do mapy drogowej. W rzeczywistości właśnie to widziałem w dokumentacji 1.2 wciąż uważanej za niestabilną ( docs.quarejs.org/api/ng.directive:ngFocus )
Edwin Dalorzo
27
@EdwinDalorzo ngFocuswydaje się być sposobem obsługi focuszdarzeń, a nie sposób ustawić ostrość na elemencie.
teleklimber
6

Zamiast tworzyć własną dyrektywę, można po prostu użyć funkcji javascript do osiągnięcia celu.

Oto przykład.

W pliku HTML:

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

W pliku javascript, na przykład w kontrolerze, w którym chcesz aktywować fokus:

document.getElementById("myInputId").focus();
użytkownik3657103
źródło
9
To nie jest „kątowy sposób” i nie radzę ludziom dotykać DOM w swoich kontrolerach.
smajl
Najlepiej nie używać wanilii w Angular, ponieważ Angular nie może jej śledzić ani synchronizować ze wszystkim innym.
Edgar Quintero
4

Jeśli chcesz tylko prostego fokusa, który był kontrolowany przez kliknięcie ng.

HTML:

<input ut-focus="focusTigger">

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

Dyrektywa:

'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();
                });
            });
        }
    }
});
TOBlender
źródło
4

Prosty, który działa dobrze z modami:

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

        link: function (scope, element, attrs)
        {


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



        }
    };
}])

Przykład

<input ng-model="your.value" focus-me-now />
Shawn Dotey
źródło
Idealne dla mnie. Dziękuję
Alexander
3

Możesz po prostu stworzyć dyrektywę, która wymusza skupienie się na dekorowanym elemencie w postLinking:

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

Następnie w swoim html:

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

Działa to w przypadku modów i przełączanych elementów ng-if, nie w przypadku ng-show, ponieważ postLinking odbywa się tylko podczas przetwarzania HTML.

ibaixas
źródło
3

Mark i Blesh mają świetne odpowiedzi; jednak Mark ma wadę, na którą zwraca uwagę Blesh (poza tym, że jest skomplikowany w implementacji), i uważam, że odpowiedź Blesha zawiera błąd semantyczny w tworzeniu usługi, która konkretnie dotyczy wysyłania żądania skupienia do interfejsu, gdy naprawdę wszystko, czego potrzebował, to sposób opóźnić wydarzenie, aż wszystkie dyrektywy będą nasłuchiwać.

Więc oto co skończyłem, co kradnie wiele z odpowiedzi Blesha, ale oddziela semantykę zdarzenia kontrolera i usługi „po załadowaniu”.

Pozwala to na łatwe podpięcie zdarzenia kontrolera do rzeczy innych niż skupienie określonego elementu, a także pozwala na obciążenie funkcji „po załadowaniu” tylko wtedy, gdy jest to potrzebne, co może nie być w wielu przypadkach.

Stosowanie

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

  afterLoad(notifyControllerEvent);
});

Źródło

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);
  }
});
joshperry
źródło
Jedyna odpowiedź, którą ja (kompletny początkujący) mogłem zrozumieć. Zamiast „afterLoad (powiadomienieControllerEvent);” użyłem „powiadomieniaControllerEvent ()”. W przeciwnym razie zakończyło się to błędami.
Vel Murugan S
3

Jest to również możliwe w użyciu ngModelController. Praca z wersją 1.6+ (nie wiem ze starszymi wersjami).

HTML

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

JS

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

-

NB: W zależności od kontekstu może być konieczne zawinięcie funkcji limitu czasu.

NB²: Przy użyciu controllerAsjest to prawie to samo. Wystarczy wymienić name="myForm"z name="vm.myForm"i w JS, vm.myForm.myText.$$element.focus();.

Ludovic Guillaume
źródło
3

Prawdopodobnie najprostsze rozwiązanie w wieku ES6.

Dodanie jednej dyrektywy liniowej powoduje, że atrybut HTML „autofocus” działa na Angular.js.

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

Teraz możesz po prostu użyć składni autofocus HTML5, takiej jak:

<input type="text" autofocus>
Tsuneo Yoshioka
źródło
@Stephane tak, więc staje się.directive('autofocus', ['$timeout', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())})])
Michael Plautz
2

Jestem tu tylko nowicjuszem, ale dzięki tej dyrektywie mogłem go uruchomić w pliku ui.bootstrap.modal :

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

i w metodzie $ modal.open użyłem następujących elementów, aby wskazać element, na którym należy ustawić fokus:

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

na szablonie mam to:

<input id="myInputId" focus />
pitervergara
źródło
2

Poniższa dyrektywa załatwiła sprawę. Użyj tego samego atrybutu HTML autofocus do wprowadzania danych.

.directive('autofocus', [function () {
    return {
        require : 'ngModel',
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.focus();
        }
    };
}])
Kishore Relangi
źródło
1
otrzymasz błąd podczas uruchamiania tego, chyba że jQuery jest dołączony, zamiast tego powinien to być element [0] .focus ()
Ryan M
2

Jeśli używasz modalInstance i masz obiekt, którego możesz użyć „następnie” do wykonania akcji po otwarciu modalu. Jeśli nie używasz modalInstance, a kodowanie jest otwarte, aby otworzyć modal, możesz użyć zdarzenia. Limit czasu $ nie jest dobrym rozwiązaniem.

Możesz zrobić (Bootstrap3):

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

W modalInstance możesz spojrzeć na bibliotekę, w jaki sposób wykonać kod po otwarciu modalu.

Nie używaj $ timeout w ten sposób, $ timeout może wynosić 0, 1, 10, 30, 50, 200 lub więcej, to zależy od komputera klienckiego i procesu otwierania modalnego.

Nie używaj $ timeout, niech metoda powie ci, kiedy możesz się skupić;)

Mam nadzieję, że to pomoże! :)

Filipe Cotrim Melo
źródło
2

Cała poprzednia odpowiedź nie działa, jeśli żądany element fokusu zostanie wstrzyknięty do szablonu dyrektywy. Poniższa dyrektywa pasuje zarówno do elementu prostego, jak i do elementu wprowadzonego przez dyrektywę (napisałem go na maszynie ). akceptuje selektor dla wewnętrznego elementu ustawialnego. jeśli potrzebujesz tylko skupić element własny - nie wysyłaj żadnych parametrów selektora do dyrektywy:

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

}

przykład użycia dla prostego elementu:

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

przykład użycia dla elementu wewnętrznego (zwykle dla dynamicznego wstrzykiwanego elementu, takiego jak dyrektywa z szablonem):

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

możesz użyć dowolnego selektora jQuery zamiast „input”

Ofir Meguri
źródło
2

Edytuję dyrektywę focusMe Marka Rajcoka, aby pracować dla wielu fokusów w jednym elemencie.

HTML:

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

w sterowniku AngularJs:

$scope.myInputFocus= true;

Dyrektywa 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);
                }
            });
        }
    };
});
Mohamad Khani
źródło
1

Chcę przyczynić się do tej dyskusji po znalezieniu lepszego rozwiązania i nie znalezieniu go, zamiast tego muszę go utworzyć.

Kryteria: 1. Rozwiązanie powinno być niezależne od zakresu kontrolera nadrzędnego, aby zwiększyć możliwość ponownego użycia. 2. Unikaj używania $ watch do monitorowania niektórych warunków, jest to zarówno powolne, zwiększa rozmiar pętli podsumowania i utrudnia testowanie. 3. Unikaj $ limitu czasu lub $ zasięgu. $ Apply (), aby uruchomić pętlę podsumowania. 4. Element wejściowy jest obecny w elemencie, w którym dyrektywa jest stosowana jako otwarta.

To rozwiązanie najbardziej mi się podobało:

Dyrektywa:

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

Mam nadzieję, że to pomoże komuś tam!

Xiaoma
źródło
Właśnie odtworzyłeś, co robi html „label”, możesz po prostu zastąpić „div focus-input” na „label” i pozbyć się dyrektyw.
piątek
1

To proste ... spróbuj tego

HTML

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

javascript

document.getElementById("ddl00").focus();
Isanka Thalagala
źródło
Jest poprawny poza kontekstem, ale nie sposób AngularJS robienia rzeczy
CodeMonkey
1

Jeśli chcesz ustawić fokus na konkretnym elemencie, możesz zastosować poniższe podejście.

  1. Utwórz usługę o nazwie 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. Wstaw go do kontrolera, z którego chcesz zadzwonić.

  3. Zadzwoń do tej usługi.

Rakesh Burbure
źródło
0

Myślę, że dyrektywa jest niepotrzebna. Użyj identyfikatora HTML i atrybutów klasy, aby wybrać wymagany element, a usługa skorzystaj z document.getElementById lub document.querySelector, aby zastosować fokus (lub odpowiedniki jQuery).

Znaczniki to standardowe dyrektywy HTML / kątowe z dodanym identyfikatorem / klasami do wyboru

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

Kontroler transmituje zdarzenie

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

W interfejsie usługa korzysta z querySelector - jeśli istnieje wiele dopasowań (powiedzmy ze względu na klasę), zwróci tylko pierwszy

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

Możesz użyć $ timeout (), aby wymusić cykl podsumowania

Johans
źródło
0

Po prostu wrzucam kawę.

app.directive 'ngAltFocus', ->
    restrict: 'A'
    scope: ngAltFocus: '='
    link: (scope, el, attrs) ->
        scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
Talasan Nicholson
źródło