Встановити фокус елемента кутовим способом


113

Шукаючи приклади того, як встановлюють елементи фокусування з кутовими, я побачив, що більшість із них використовує якусь змінну для перегляду, а потім встановить фокус, і більшість з них використовує одну різну змінну для кожного поля, на яке вони хочуть встановити фокус. У формі з великою кількістю полів, що передбачає безліч різних змінних.

Маючи на увазі спосіб jquery, але бажаючи зробити це угловим способом, я прийняв рішення, що ми задаємо фокус на будь-якій функції за допомогою ідентифікатора елемента, тому, оскільки я є абсолютно новим у кутовому, я хотів би отримати деякі думки, якщо таким чином правильно, є проблеми, будь-що, все, що могло б допомогти мені зробити це кращим чином у кутку.

В основному я створюю директиву, яка переглядає значення області, визначеного користувачем з директивою, або за замовчуванням focusElement, і коли це значення те саме, що ідентифікатор елемента, цей елемент встановлює фокус сам.

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

Вхід, який повинен отримати фокус з якоїсь причини, зробить це так

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

Тут будь-який елемент, щоб встановити фокус:

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

І приклад функції, яка встановлює фокус:

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

Це гарне рішення в кутовому? Чи є у нього проблеми, які зі своїм поганим досвідом я ще не бачу?

Відповіді:


173

Проблема вашого рішення полягає в тому, що воно не працює добре, якщо він прив'язується до інших директив, що створює нову область застосування, наприклад ng-repeat. Кращим рішенням буде просто створити сервісну функцію, яка дозволяє вам обов'язково фокусувати елементи в межах своїх контролерів або декларативно фокусувати елементи в html.

DEMO

JAVASCRIPT

Сервіс

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

Контролер

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

Мені дуже подобається таке рішення. Чи можете ви пояснити причину використання $ timeout трохи більше? Чи є причина, по якій ви його використовували, через "кутову річ" або "річ DOM"?
користувач1821052

Він гарантує, що він працює після будь-яких циклів дайджесту, які є кутовими, але це виключає цикли дайджесту, які впливають після асинхронної дії, що виконується після таймауту.
ryeballar

3
Дякую! Для тих, хто цікавиться, де на це посилається у кутових документах, ось посилання (взяло мене назавжди, щоб знайти)
користувач1821052

@ryeballar, Дякую !. Приємне просте рішення. Тільки питання, хоча. Чи можу я використовувати фабрику, створену за допомогою атрибута, а не чекати, коли станеться якась подія?
Пратік Гайквад

4
Це божевільний обсяг роботи, який необхідний у кутовому просто, щоб зосередити вхід.
Бруно Сантос

19

Щодо цього рішення, ми могли б просто створити директиву та приєднати її до елемента DOM, який повинен отримати фокус, коли задана умова буде виконана. Дотримуючись цього підходу, ми уникаємо приєднання контролера до ідентифікаторів елемента DOM.

Прикладна директива коду:

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

Можливе використання

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

1
що станеться, коли myConditionзмінну $ range вже встановлено на true, і тоді користувач вирішить зосередитись на іншому елементі, чи зможете ви ще повторно запустити фокус, коли myConditionце вже правда, ваш код спостерігає за змінами атрибута, focusOnConditionале він не спрацьовує, коли значення, яке ви намагаєтесь змінити, все одно те саме.
райбаллар

Я збираюся оновити зразок, у нашому випадку у нас є дві радіо кнопки, і ми перемикаємо прапор на справжній або хибний залежно від значення, ви можете просто змінити прапор myCondition на true або false
Braulio

Схоже на загальне рішення. Краще, ніж залежно від ідентифікаторів. Мені це подобається.
mortb

У випадку, якщо хтось інший спробує це, і це не працює, мені довелося змінити element.focus (); до елемента [0] .фокус ();
Адріан Карр

1
Це рішення є набагато більш "кутовим способом", ніж хак на базі id вище.
setec

11

Мені подобається уникати пошуку DOM, годинників та глобальних випромінювачів, коли це можливо, тому я використовую більш прямий підхід. Використовуйте директиву, щоб призначити просту функцію, орієнтовану на елемент директиви. Потім зателефонуйте до цієї функції, де потрібно в межах контролера.

Ось спрощений підхід для приєднання його до сфери застосування. Дивіться повний фрагмент для обробки синтаксису контролера.

Директива:

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

і в html:

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

або в контролері:

$scope.focusOnSaveInput();

Відредаговано, щоб надати більше пояснень щодо причини такого підходу та розширити фрагмент коду для використання контролером.


Це дуже приємно, і добре працює для мене. Але тепер у мене є набір входів за допомогою ng-repeat, і я хочу лише встановити функцію фокусування для першого. Будь-яка ідея, як я можу умовно встановити функцію фокусування на <input>основі, $indexнаприклад?
Гаррет Вілсон

Радий, що це корисно. Мій кутовий 1 трохи іржавий, але ви повинні мати можливість додати інший атрибут до вводу, як-от assign-focus-function-if="{{$index===0}}", а потім як перший рядок директиви вийти рано, перш ніж призначити функцію, якщо це неправда: if (attr.assignFocusFunctionIf===false) return; Примітка Я перевіряю, чи це явно, falseа не просто фальси, тому директива все одно буде працювати, якщо цей атрибут не визначений.
cstricklan

Контролер - як набагато простіше з лодашем. _.set(scope, attributes.focusOnSaveInput, function() { element.focus(); }).
Атомоск

9

Ви можете спробувати

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

напр.

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

це працює для мене.


4
Примітка. Це буде працювати лише в тому випадку, коли використовується повний jQuery, а не покладається на jqLite, вбудований у Angular. Дивіться docs.angularjs.org/api/ng/function/angular.element
Джон Рікс

4
Це спосіб jQuery робити це, а не кутовий спосіб. Питання конкретно запитує, як це зробити кутовим способом.
прощання

4

Іншим варіантом буде використання вбудованої архітектури Pub-sub для Angular, щоб сповістити вашу директиву про фокусування. Подібно до інших підходів, але тоді він не пов'язаний безпосередньо із властивістю, а натомість прослуховує його обсяг для певного ключа.

Директива:

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

Контролер:

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

3

Я вважав за краще використовувати вираз. Це дозволяє мені робити такі речі, як фокусування на кнопці, коли поле дійсне, досягає певної довжини і, звичайно, після завантаження.

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

У складній формі це також зменшує необхідність створення додаткових змінних областей для фокусування.

Дивіться https://stackoverflow.com/a/29963695/937997

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.