Как установить фокус на поле ввода?

759

Что такое «угловой способ», чтобы установить фокус на поле ввода в AngularJS?

Более конкретные требования:

  1. Когда модальное окно открыто, установите фокус на предварительно определенный <input>внутри этого модального элемента.
  2. Каждый раз <input>становится видимым (например, нажав на какую-то кнопку), установите фокус на нем.

Я пытался выполнить первое требование с помощью autofocus, но это работает только тогда, когда модал открывается в первый раз, и только в определенных браузерах (например, в Firefox это не работает).

Любая помощь будет оценена.

Миша Морошко
источник

Ответы:

579
  1. Когда модальное окно открыто, установите фокус на предварительно определенный <input> внутри этого модального окна.

Определите директиву и задайте ей $ watch свойство / триггер, чтобы он знал, когда нужно сфокусировать элемент:

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

$ Timeout, кажется, необходим, чтобы дать модальное время для рендеринга.

'2.' Каждый раз, когда <input> становится видимым (например, нажатием какой-либо кнопки), установите фокус на него.

Создайте директиву, по сути, похожую на приведенную выше Следите за некоторым свойством контекста, и когда оно станет истинным (установите его в вашем обработчике ng-click), выполните element[0].focus(). В зависимости от вашего варианта использования вам может понадобиться или не потребоваться тайм-аут в $ для этого:

<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


Обновление 7/2013 : я видел, как несколько человек использовали мои оригинальные директивы для области видимости, а затем столкнулись с проблемами во встроенных полях ввода (т. Е. В поле ввода в модальном поле). Директива без новой области (или, возможно, с новой дочерней областью) должна облегчить некоторые проблемы. Поэтому выше я обновил ответ, чтобы не использовать изолирующие области. Ниже оригинальный ответ:

Оригинальный ответ для 1., используя область выделения:

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

Плункер .

Исходный ответ для 2. с использованием изолированной области:

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

Плункер .

Так как нам нужно сбросить свойство trigger / focusInput в директиве, '=' используется для двусторонней привязки данных. В первой директиве «@» было достаточно. Также обратите внимание, что при использовании «@» мы сравниваем значение триггера с «истиной», поскольку @ всегда приводит к строке.

Марк Райкок
источник
1
Смотрите также директиву @ focus Джоша: stackoverflow.com/a/14859639/215945 Он не использовал изолированную область в своей реализации.
Марк Райкок
2
@MarkRajcok просто любопытно по этому поводу: эта версия работает, но если я установлю значение ng-modelв поле ввода, значение модели будет потеряно, когда я использую эту директиву без использования области видимости. Проблема не произойдет, если я попробую версию Джоша без изолированной области видимости. Все еще новичок, и я хотел бы понять разницу. Вот Plunker, который показывает это.
Сунил Д.
1
Я обнаружил, что # 1 очень хорошо работает с AngularJS 1.0.6. Однако, когда я работал под отладчиком, я заметил, что каждый раз, когда я закрывал и снова открывал свой модал, я видел еще один дополнительный вызов функции, которая устанавливает фокус, чем раньше. Я слегка изменил эту функцию, чтобы отменить привязку часов value != "true", и это, казалось, решило мою проблему.
Эндрю Браун
1
Итак ... на странице с отслеживанием состояния у меня было то же самое, потому что $ watch - это $ watch, а угловые разработчики любят $ watch ... ну, я столкнулся с проблемой, мистер @MarkRajcok, проблема, которую я решил с помощью (основного) решения, которое я предложено ниже.
Бен Леш
3
хм для меня код работает, так как я вижу, что он работает правильно, а element [0] - это правильный элемент управления. однако .focus () не вызывает фокусировку. возможно начальная загрузка или что-то еще мешает этому?
Sonic Soul
264

(РЕДАКТИРОВАТЬ: я добавил обновленное решение ниже этого объяснения)

Марк Райкок - человек ... и его ответ - правильный ответ, но у него был недостаток (извините, Марк) ...

... Попробуйте использовать логическое значение, чтобы сфокусироваться на входе, затем размыть вход, затем попробуйте использовать его, чтобы снова сфокусировать ввод. Это не сработает, если вы не сбросите логическое значение на false, затем $ digest, а затем не сбросите его на true. Даже если вы используете сравнение строк в своем выражении, вы будете вынуждены изменить строку на что-то другое, $ digest, а затем изменить ее обратно. (Это было решено с помощью обработчика событий размытия.)

Поэтому я предлагаю это альтернативное решение:

Используйте событие, забытое свойство Angular.

В конце концов, JavaScript любит события. События по своей сути слабо связаны, и, что еще лучше, вы избегаете добавления других $ watch к вашему $ digest.

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

Так что теперь вы можете использовать это так:

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

а затем в любом месте вашего приложения ...

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

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

Это потрясающе, потому что вы можете делать все что угодно с чем-то вроде этого. С одной стороны, вы можете связать с событиями, которые уже существуют. Во-вторых, вы начинаете делать что-то умное, публикуя события в разных частях вашего приложения, на которые могут подписаться другие части вашего приложения.

Во всяком случае, этот тип вещей кричит мне «событие, управляемое». Я думаю, как разработчики Angular, мы очень стараемся вбить колышки в форме $ scope в отверстия формы событий.

Это лучшее решение? Я не знаю. Это решение.


Обновленное решение

После комментария @ ShimonRachlenko ниже я немного изменил свой метод. Теперь я использую комбинацию службы и директивы, которая обрабатывает событие "за сценой":

Помимо этого, это тот же принцип, изложенный выше.

Вот быстрое демо Plunk

Применение

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

Источник

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);
    });
  }
});
Бен Леш
источник
3
Вам нужно обернуть вызов $broadcastс , $timeoutесли вы хотите , чтобы это работало на входе контроллера. В противном случае хорошее решение.
Шимон Рахленко
@ShimonRachlenko - Спасибо. Но я не уверен, что вы подразумеваете под таймаутом $. Если бы я хотел вещать во время обработки конструктора контроллера, я бы тогда вещал прямо сейчас. Тайм-аут не будет ничего делать, кроме как отложить трансляцию до более позднего выполнения в цикле событий.
Бен Леш
1
Да, и этого достаточно для инициализации директивы. Повсеместно событие транслируется до того, как директива начинает его слушать. Опять же, это необходимо, только если вы хотите активировать свою директиву при входе на страницу.
Шимон Рахленко
2
Ты прав. Я думаю, я не использовал это, чтобы сосредоточиться на нагрузке. Я обновлю ответ более надежным.
Бен Леш
1
Это, безусловно, самое изящное, «угловое» решение. Хотя я в основном только что скопировал код, когда впервые столкнулся с этой проблемой, я рад, что вы создали для него модуль! Я искренне думаю, что, возможно, стоит попытаться получить это в основном под углом.
Рандольфо
241

Я нашел некоторые другие ответы слишком сложными, когда все, что вам действительно нужно, это

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

использование

<input name="theInput" auto-focus>

Мы используем тайм-аут, чтобы позволить объектам в домене визуализироваться, даже если он равен нулю, он, по крайней мере, ждет этого - таким образом это работает в модальных моделях и еще много чего.

ecancil
источник
1
Есть несколько способов сделать это, один из возможных способов, который действительно прост и понятен, состоит в том, чтобы в области (контроллер здесь) установить идентификатор элемента, на который вы хотите сфокусироваться, когда вы нажимаете кнопку, затем в Директива просто послушай это. В этом случае вам не нужно было бы помещать директиву где-то конкретно, просто где-то на этой странице (я использовал подобные директивы наблюдателя с успехом в прошлом, особенно с фокусировкой и прокруткой) - тогда если вы ' повторное использование jquery (сделало бы это проще), просто найдите этот элемент id и сфокусируйте его
ecancil
14
Решение с нулевым тайм-аутом не работает для меня, если вход находится во всплывающем моде. Но даже 10 мс
решают
@ecancil: мне нравится ваш подход, потому что он самый простой, но вам нужно установить время ожидания ~ 500 мс в IE, потому что анимация модального появления приводит к миганию курсора за пределами ввода. Я не знаю хорошего способа, чтобы это произошло, когда анимация заканчивается, поэтому я грубо форсирую это с помощью 500 мс.
Dev93
не будет работать, если вам нужно извлечь больше данных из базы данных, нужно подождать, пока контроллер завершит извлечение данных, поэтому добавьте достаточно времени вместо 0
Али Адрави
1
Для таких как @Ade, которые хотят использовать это с простым ng-click: скажем, нажатие кнопки имеет ng-click="showInput = !showInputна вашем входе. Затем, на ваш фактический вклад, добавьте ng-if="showInput". Переключение кнопки заставит директиву перезапускаться каждый раз. У меня была проблема с этим, поскольку я использовал, ng-showкоторый является неправильным подходом.
JVG
91

HTML имеет атрибут autofocus.

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

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

Райрон Виктор
источник
29
К сожалению, это работает только один раз, если вообще. Я использовал это в Angular Edit на месте , и он отлично работал в первый раз в Chrome, а не в Firefox. В Chrome, когда я щелкаю другой элемент для редактирования на месте или даже снова на том же элементе, он больше не фокусируется. Документы утверждают, что он работает при загрузке страницы, что происходит только один раз в приложении Angular. Если бы это было так просто, мы бы все сидели на пляже и зарабатывали 20%!
Ноктюрно
62

Вы также можете использовать функциональность jqlite, встроенную в angular.

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

JordanC
источник
2
Без загруженного jquery: angular.forEach (document.querySelectorAll ('. Selector'), function (elem) {elem.focus ();});
Дэн Бенами
29
Разве это не плохая практика, чтобы поместить это в контроллер?
VitalyB
2
Если размещение этой строки jqlite внутри контроллера - плохая практика, не лучше ли поместить эту строку jqlite в директиву?
спорт,
3
Я получаю это:Looking up elements via selectors is not supported by jqLite!
Мария Инес Парнисари
1
@VitalyB У меня был случай, когда мне нужно было размыть элемент после вызова ajax. Я мог бы добавить только одну строку чистого JS в обратный вызов внутри контроллера или построить целую директиву исключительно для размытия этого ввода. Я просто чувствовал, что это было слишком много хлопот, поэтому я выбрал первый вариант.
Себастьян
55

Это хорошо работает и угловой способ фокусировки ввода управления

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

Это хотя и не чисто угловой способ выполнения задачи, но синтаксис следует угловому стилю. Jquery играет косвенную роль и напрямую обращается к DOM, используя Angular (jQLite => JQuery Light).

При необходимости этот код можно легко поместить в простую угловую директиву, где элемент доступен напрямую.

Санджив Сингх
источник
Я не уверен, что это отличный подход для ViewModel-приложений. В Angular это должно быть сделано с помощью директив.
Агат
Например, если кто-то меняет текстовое поле A, и вам нужно показать всплывающее окно и установить фокус на другое текстовое поле B.
Санджив Сингх,
1
Я получаю эту ошибку при использовании этого:Looking up elements via selectors is not supported by jqLite!
JVG
6
Насколько я могу судить, вам сначала нужно загрузить jQuery в качестве зависимости, и в этом случае он angular.elementстановится оболочкой для $() / jQuery(). Так что без этого это не сработает, и вы все равно просто используете jQuery (но поправьте меня, если я ошибаюсь)
JVG
@Jascination: jqLite разработан для удаления зависимости от jQuery. Вам не нужен целый jQuery для доступа к элементу в DOM. Но если у вас установлен jQuery, то Angular будет ссылаться на jQuery. Проверьте это: docs.angularjs.org/api/angular.element
Санджив Сингх
31

Я не думаю, что $ timeout - это хороший способ сфокусировать элемент на создании. Вот метод, использующий встроенную угловую функциональность, вырытую из темных глубин угловых документов. Обратите внимание, как атрибут «ссылка» может быть разделен на «pre» и «post» для функций pre-link и post-link.

Рабочий пример: 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 />

Полная директива AngularJS Документы: https://docs.angularjs.org/api/ng/service/$compile

Коди Мониз
источник
2
Нужно было обернуть элемент [0] .focus () в $ timeout, чтобы он работал для меня.
Кодин
@bbodenmiller Мой модал применяет функцию постепенного исчезновения, когда элемент создается, он невидим (100% прозрачен), поэтому браузер блокирует вызов фокуса без уведомления. Видимо, пройденные миллисекунды позволяют сосредоточиться на почти прозрачном, но видимом входе / кнопке.
Snowindy
1
согласно документам, функции "link" по умолчанию являются "postLink". Также смотрите: bennadel.com/blog/...
Джоди Tate
17

Вот мое оригинальное решение:

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

И HTML:

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

Что оно делает:

Он фокусирует ввод, когда он становится видимым с помощью ng-show. Нет необходимости использовать $ watch или $ здесь.

Edhowler
источник
На самом деле я считаю, что {{isVisible}} в любом случае создает часы, поэтому выражение «No use of $ watch» неверно.
Масимпло
Да, я думаю, что вы правы. Но это все еще служит простым способом сделать это.
Edhowler
17

Недавно я написал директиву о двухстороннем связывании фокуса, как и модель.

Вы можете использовать директиву focus следующим образом:

<input focus="someFocusVariable">

Если вы сделаете переменную области видимости someFocusVariable trueгде-нибудь в вашем контроллере, ввод будет сфокусирован. И если вы хотите «размыть» ваш ввод, тогда для someFocusVariable можно установить значение false. Это как первый ответ Марка Райкока, но с двусторонним связыванием.

Вот директива:

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

Применение:

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

Вот скрипка:

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

Умут Бензер
источник
Это должен быть правильный ответ. Он охватывает все варианты использования.
Кунал
11

Для тех, кто использует Angular с плагином Bootstrap:

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

Вы можете подключиться к openedобещанию модального экземпляра:

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

modalInstance.result.then(function ( etc...
BPH
источник
Хорошо! Однако, в моем случае, $timeoutс 50msнуждается в вместо 0.
lk_vc
8

Я нашел полезным использовать общее выражение. Таким образом, вы можете делать такие вещи, как автоматическое перемещение фокуса, когда введенный текст действителен

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

Код для директивы:

.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
                        }
                    }
                });
            }
        }
    };
});
Уинри
источник
7

Не воскрешать зомби и не вставлять мою собственную директиву (хорошо, это именно то, что я делаю):

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
        };
    }
})();
Джон Хиб
источник
Сейчас я использую эту директиву, и она прекрасно работает, решает общую проблему и не слишком сложна. В поисках решения без тайм-аута. Продолжайте находить комментарии о том, что фокус находится «на дорожной карте» для Angular 1.1 и 1.2, но я использую 2.x и все еще не могу управлять фокусом. Хех.
tekHedd
6

Во-первых, официальный способ сосредоточиться на дорожной карте для 1.1 . Между тем, вы можете написать директиву для реализации настроек фокуса.

Во-вторых, для фокусировки на элементе после того, как он стал видимым, требуется обходной путь. Просто отложите ваш вызов элемента focus () с помощью $timeout.

Поскольку существует та же проблема controller-modizes-DOM для фокусировки, размытия и выделения, я предлагаю иметь ng-targetдирективу:

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

Угловая тема здесь: http://goo.gl/ipsx4 , а более подробная информация опубликована здесь: http://goo.gl/4rdZa

Следующая директива создаст .focus()функцию внутри вашего контроллера, как указано вашим ng-targetатрибутом. (Это создает .blur()и .select()тоже.) Демо: http://jsfiddle.net/bseib/WUcQX/

broc.seib
источник
+1 для ссылки на дорожную карту. Фактически, я только что видел это в документации 1.2, которая все еще считается нестабильной ( docs.angularjs.org/api/ng.directive:ngFocus )
Эдвин Далорсо
27
@EdwinDalorzo ngFocus- это способ обработки focusсобытий, а не способ установить фокус на элементе.
телекалибер
6

Вместо создания своей собственной директивы можно просто использовать функции javascript для достижения цели.

Вот пример.

В HTML-файл:

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

В файле javascript, например, в контроллере, где вы хотите активировать фокус:

document.getElementById("myInputId").focus();
user3657103
источник
9
Это не «угловой путь» и не советуйте людям прикасаться к DOM в своих контроллерах.
Смайл
Лучше не использовать ваниль в Angular, потому что Angular не может ни отслеживать его, ни синхронизировать со всем остальным.
Эдгар Кинтеро
4

Если вы просто хотели простой фокус, которым управлял нг-клик.

Html:

<input ut-focus="focusTigger">

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

Директива:

'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
источник
4

Простой, который хорошо работает с модалами:

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

        link: function (scope, element, attrs)
        {


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



        }
    };
}])

пример

<input ng-model="your.value" focus-me-now />
Шон Дотей
источник
Идеально для меня. Спасибо
Александр
3

Вы можете просто создать директиву, которая заставляет сосредоточиться на украшенном элементе на postLinking:

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

Тогда в вашем HTML:

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

Это будет работать для модальных элементов и переключаемых элементов ng-if, а не для ng-show, поскольку postLinking происходит только при обработке HTML.

ibaixas
источник
3

У Марка и Блеша прекрасные ответы; однако у Марка есть недостаток, на который указывает Блеш (помимо того, что он сложен для реализации), и я чувствую, что ответ Блеша имеет семантическую ошибку при создании службы, которая конкретно направляет запрос фокуса на внешний интерфейс, когда на самом деле все, что ему нужно, - это способ отложить событие, пока все директивы не будут прослушаны.

Итак, вот что я в итоге сделал, что во многом зависит от ответа Blesh, но разделяет семантику события контроллера и службы «после загрузки».

Это позволяет легко перехватывать событие контроллера для других вещей, помимо простой фокусировки на конкретном элементе, а также позволяет нести служебную нагрузку функции «после загрузки», только если это необходимо, чего во многих случаях может не быть.

Применение

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

  afterLoad(notifyControllerEvent);
});

Источник

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
источник
Единственный ответ, который я (полный новичок) мог понять. Вместо «afterLoad (notifyControllerEvent);» я использовал «notifyControllerEvent ()». В противном случае все закончилось некоторыми ошибками.
Вел Муруган С
3

Это также возможно использовать ngModelController. Работа с 1.6+ (не знаю со старыми версиями).

HTML

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

JS

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

-

NB. В зависимости от контекста вам, возможно, придется включить функцию тайм-аута.

NB²: При использовании controllerAsэто почти то же самое. Просто замените name="myForm"на name="vm.myForm"и в JS vm.myForm.myText.$$element.focus();.

Людовик Гийом
источник
3

Наверное, самое простое решение по возрасту ES6.

Добавление следующей директивы liner делает HTML-атрибут autofocus эффективным в Angular.js.

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

Теперь вы можете просто использовать синтаксис автофокуса HTML5, например:

<input type="text" autofocus>
Цунео Йошиока
источник
@ Стефан, да, тогда это становится.directive('autofocus', ['$timeout', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())})])
Майкл Плаутс
2

Просто новичок здесь, но я был в состоянии заставить его работать в 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();
                    });
                }
            });
        }
    };
});

и в методе $ modal.open я использовал следующие элементы, чтобы указать элемент, на который следует поместить фокус:

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

на шаблоне у меня есть это:

<input id="myInputId" focus />
pitervergara
источник
2

Следующая директива сработала для меня. Используйте тот же атрибут html автофокуса для ввода.

.directive('autofocus', [function () {
    return {
        require : 'ngModel',
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.focus();
        }
    };
}])
Кишоре Реланги
источник
1
вы получите ошибку при запуске этого, если jQuery не включен, вместо этого он должен быть элементом [0] .focus ()
Райан М
2

Если вы используете modalInstance и у вас есть объект, вы можете использовать «затем» для выполнения действий после открытия модального. Если вы не используете modalInstance и жестко запрограммированы, чтобы открыть модал, вы можете использовать событие. Тайм-аут $ не является хорошим решением.

Вы можете сделать (Bootstrap3):

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

В modalInstance вы можете посмотреть на библиотеку, как выполнить код после открытия модального.

Не используйте $ timeout, как это, $ timeout может быть 0, 1, 10, 30, 50, 200 или более, это будет зависеть от клиентского компьютера и процесса открытия модального режима.

Не используйте $ timeout, пусть метод скажет вам, когда вы можете сосредоточиться;)

Я надеюсь, что это поможет! :)

Филипе Котрим Мело
источник
2

Весь предыдущий ответ не работает, если нужный элемент фокуса вставлен в шаблон директивы. Следующая директива подходит как для простого элемента, так и для элемента, вставленного в директиву (я написал ее на машинописи ). он принимает селектор для внутреннего фокусируемого элемента. если вам просто нужно сфокусировать элемент self - не отправляйте параметр селектора в директиву:

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

}

пример использования для простого элемента:

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

пример использования для внутреннего элемента (обычно для динамического внедренного элемента, такого как директива с шаблоном):

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

Вы можете использовать любой селектор jQuery вместо «ввода»

Офир Мегури
источник
2

Я редактирую директиву focusMe Марка Райкока для работы с несколькими фокусами в одном элементе.

HTML:

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

в контроллере AngularJs:

$scope.myInputFocus= true;

Директива 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);
                }
            });
        }
    };
});
Мохамад Хани
источник
1

Я хочу внести свой вклад в эту дискуссию после того, как искал лучшее решение и не нашел его, а вместо этого создал его.

Критерии: 1. Решение должно быть независимым от области действия родительского контроллера для увеличения возможности повторного использования. 2. Избегайте использования $ watch для отслеживания некоторых условий, это является медленным, увеличивает размер цикла дайджеста и усложняет тестирование. 3. Избегайте $ timeout или $ scope. $ Apply () для запуска дайджест-цикла. 4. Элемент ввода присутствует внутри элемента, в котором используется директива open.

Это решение мне понравилось больше всего:

Директива:

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

Я надеюсь, что это поможет кому-то там!

xiaoma
источник
Вы просто воссоздали, что делает html «label», вы можете просто заменить «div focus-input» на «label» и избавиться от директивы.
первое
1

Это легко .. попробуйте это

HTML

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

Javascript

document.getElementById("ddl00").focus();
Исанка Талагала
источник
Это правильно вне контекста, но не AngularJS способ делать вещи
CodeMonkey
1

Если вы хотите установить фокус на конкретный элемент, вы можете использовать подход ниже.

  1. Создайте сервис под названием 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. Введите его в контроллер, откуда вы хотите позвонить.

  3. Позвоните в эту службу.

Ракеш Бурбур
источник
0

Я думаю, что директива не нужна. Используйте HTML id и атрибуты класса, чтобы выбрать необходимый элемент, и попросите службу использовать document.getElementById или document.querySelector для применения фокуса (или эквивалентов jQuery).

Разметка - это стандартные HTML / угловые директивы с добавленными идентификаторами / классами для выбора.

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

Контроллер транслирует событие

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

В UI сервис использует querySelector - если есть несколько совпадений (скажем, из-за класса), он вернет только первое

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

Вы можете использовать $ timeout () для запуска цикла дайджеста.

Johans
источник
0

Просто добавляю кофе.

app.directive 'ngAltFocus', ->
    restrict: 'A'
    scope: ngAltFocus: '='
    link: (scope, el, attrs) ->
        scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
Таласан Николсон
источник