Як встановити фокус на полі введення?


759

Який "кутовий спосіб" для фокусування на полі введення в AngularJS?

Більш конкретні вимоги:

  1. Коли Modal відкрито, встановіть фокус на заздалегідь визначеному <input>всередині цього Модалу.
  2. Щоразу <input>стає видно (наприклад, натиснувши якусь кнопку), встановіть фокус на ній.

Я намагався досягти першої вимоги за допомогою autofocus, але це працює лише тоді, коли Modal відкритий вперше, і лише в певних браузерах (наприклад, у 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));
            });
        }
    };
}]);

Плункер

Очевидно, потрібний $ 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;
          //});
        }
      });
    }
  };
});

Плункер


Оновлення 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;
          //});
        }
      });
    }
  };
});

Плункер .

Оскільки нам потрібно скинути властивість тригер / focusInput у директиві, '=' використовується для двостороннього прив’язки даних. У першій директиві "@" було достатньо. Також зауважте, що при використанні "@" ми порівнюємо значення тригера з "true", оскільки @ завжди призводить до рядка.


1
Дивіться також директиву @ фокус Джоша про "фокус": stackoverflow.com/a/14859639/215945 Він не використовував ізоляторну область в своїй реалізації.
Марк Райкок

2
@MarkRajcok цікаво з цього приводу: ця версія працює, але якщо я встановив ng-modelполе для введення, значення моделі втрачається, коли я використовую цю директиву з використанням області ізоляції. Проблема не трапиться, якщо я спробую версію Джоша вимкнути область ізоляції. Ще новачок, і я хотів би зрозуміти різницю. Ось Plunker, який це показує.
Суніль Д.

1
Я виявив, що №1 дуже добре працює з AngularJS 1.0.6. Однак, працюючи під налагоджувачем, я помітив, що кожного разу, коли я відхиляю і знову відкриваю свій модал, я бачу ще один додатковий виклик функції, що задає фокус, ніж час раніше. Я трохи змінив цю функцію, щоб відключити годинник, коли value != "true"це з'явилося для вирішення моєї проблеми.
Ендрю Браун

1
Отже ... на важливій сторінці у мене відбулося те саме, тому що $ watch - це $ watch, а кутові розробники люблять $ watch ... ну, я потрапив на проблему, пане @MarkRajcok, проблему, яку я вирішив із (базовим) рішенням I запропоновано нижче.
Бен Леш

3
хм для мене код працює, так як у мене видно, що він виконує належним чином, а елемент [0] - це правильне управління. однак .focus () не запускає фокус. можливо, завантажувач чи щось інше заважає цьому?
Sonic Soul

264

(EDIT: я додав оновлене рішення нижче цього пояснення)

Марк Rajcok є людина ... і його відповідь є правильною відповіддю, але він вже мав дефект (вибачте Mark) ...

... Спробуйте скористатися булевою формою, щоб зосередитись на вході, потім розмийте вхід, а потім спробуйте використовувати його, щоб знову фокусувати вхід. Він не буде працювати, якщо ви не скинете булеве значення false, потім $ digest, а потім відновите його до true. Навіть якщо ви використовуєте порівняння рядків у своєму виразі, ви змушені будете змінити рядок на щось інше, $ digest, а потім змінити його назад. (Це вирішено з обробником події розмиття.)

Тому я пропоную це альтернативне рішення:

Використовуйте подію, забуту функцію Angular.

JavaScript все-таки любить події. Події за своєю суттю слабко поєднані, а ще краще, ви уникаєте додавати ще $ watch у свій дайджест $.

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, ми дуже намагаємось забити штифти у формі $ range у отвори форми подій.

Це найкраще рішення? Я не знаю. Це рішення.


Оновлене рішення

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

Крім цього, це той самий принцип, викладений вище.

Ось швидкий демонстраційний планк

Використання

<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 - спасибі Але я не впевнений, що ви маєте на увазі під терміном $ timeout. Якби я хотів транслювати, коли конструктор контролера оброблявся, я б просто транслював прямо зараз. Тайм-аут нічого не зробить, але відкладе трансляцію на наступне виконання у циклі подій.
Бен Леш

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>

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


1
Існує кілька способів зробити це. Одним з можливих способів, який дійсно є прямим вперед та легким, буде те, щоб на область (контролер тут) встановити ідентифікатор предмета, на який ви хочете зосередитись, натискаючи кнопку, а потім у Директива просто слухати це. У цьому випадку вам не доведеться розміщувати директиву де-небудь зокрема, лише десь на цій сторінці (я раніше використовував подібні директиви спостерігачів з успіхом, особливо з фокусуванням речей і прокручування речей) - Тоді якщо ви ' повторно використовуючи jquery (зробив би це простіше), просто знайдіть ідентифікатор елемента та сфокусуйте його
ecancil

14
Розв’язання з нульовим тайм-аутом не працює для мене, якщо введення знаходиться в спливаючому режимі. Але навіть 10 мс
вирішують

@ecancil: Мені подобається ваш підхід, тому що він найпростіший, але вам потрібно встановити час IE ~ 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
На жаль, це працює лише один раз, якщо взагалі. Я використовував це в кутовій редагуванні на місці , і це чудово спрацювало вперше в Chrome, зовсім не в Firefox. У Chrome, коли я натискаю інший елемент, щоб змінити його на місці, або навіть на тому ж елементі, він більше не набуває фокусу. Документи констатують, що він працює при завантаженні сторінки, що відбувається лише один раз у програмі Angular. Якби це було просто, ми всі сиділи на пляжі і заробляли 20%!
Ноктюрно

62

Ви також можете використовувати функцію jqlite, вбудовану в кутову.

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


2
Без завантаженого jquery: angular.forEach (document.querySelectorAll ('. Selector'), function (elem) {elem.focus ();});
Dan Benamy

29
Хіба це не погана практика здавати це в контролер?
ВіталійБ

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 є гарним способом зосередити елемент на створенні. Ось метод, що використовує вбудований кутовий функціонал, виритий із похмурих глибин кутових документів. Зверніть увагу, як атрибут "link" може бути розділений на "pre" та "post", для функцій попереднього посилання та після посилання.

Приклад роботи: 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
за документами функціями "посилання" за замовчуванням є "postLink". також дивіться: bennadel.com/blog/…
jody tate

17

Ось моє оригінальне рішення:

плункер

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 чи $.


Насправді я вважаю, що {{isVisible}} так чи інакше створює годинник, тому вислів "Немає використання $ watch" невірно.
masimplo

Так, я думаю, ти маєш рацію. Але це все ще служить простим способом зробити це.
Edhowler

17

Я написав двосторонню обов'язкову директиву щодо фокусування, як і модель недавно.

Ви можете використовувати директиву щодо фокусування так:

<input focus="someFocusVariable">

Якщо ви зробите деяку змінну області застосування FoocusVariable true в будь-якій точці свого контролера, вхід зосереджується. І якщо ви хочете "розмити" свій вхід, тоді деякий FoocVariable може бути встановлений на значення 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...

Добре! Однак в моєму випадку, $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
        };
    }
})();

Зараз я використовую цю директиву, і вона чудово працює, вирішує загальну проблему і не є надмірно складною. Розпочав шукати рішення, яке не вимагає $. Продовжуйте знаходити коментарі, говорячи про те, що фокус є "на дорожній карті" для кутових 1.1 і 1.2, але я використовую 2.x і досі немає управління фокусом. Хе.
tekHedd

6

По-перше, офіційний спосіб зробити фокус - це дорожня карта для 1.1 . Тим часом, ви можете написати директиву, щоб реалізувати фокус налаштування.

По-друге, для встановлення фокусу на елементі після того, як він став видимим, в даний час потрібно вирішити. Просто затримайте ваш дзвінок на фокус елемента () за допомогою a $timeout.

Оскільки для фокусування, розмиття та вибору існує одна і та ж контролер, що модифікує 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/


+1 для довідкової довідкової карти. Насправді я щойно бачив це в документації 1.2, яка все ще вважається нестабільною ( docs.angularjs.org/api/ng.directive:ngFocus )
Едвін Далорцо,

27
@EdwinDalorzo ngFocusвидається способом обробляти focusподії, а не способом встановити фокус на елементі.
teleclimber

6

Замість того, щоб створювати власну директиву, можна просто використовувати функції JavaScript для досягнення фокусу.

Ось приклад.

У файлі html:

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

У файлі javascript, наприклад, у контролері, де потрібно активувати фокус:

document.getElementById("myInputId").focus();

9
Це не «кутовий спосіб», і не радимо людям торкатися DOM у своїх контролерах.
smajl

Найкраще не використовувати ваніль у Angular, оскільки Angular не може її відстежувати і не підтримувати синхронізацію з усім іншим.
Едгар Кінтеро

4

Якщо ви просто хотіли простий фокус, який контролювався натисканням кнопки ng.

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

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.


3

Марк і Блеш мають чудові відповіді; однак у Марка є недолік, на який вказує Блеш (окрім того, що його складно реалізувати), і я відчуваю, що відповідь Блеша має семантичну помилку в створенні сервісу, який стосується конкретного направлення запиту на фокус у фронтенд, коли насправді все, що йому потрібно, було способом відкладіть подію, поки всі директиви не прислухалися.

Отже, ось, що я закінчив, який багато вкрадає з відповіді Блеша, але зберігає семантику події контролера та послуги "після завантаження".

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

Використання

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

Єдина відповідь, яку я (повний новачок) міг зрозуміти. Замість "afterLoad (notifyControllerEvent);", я використовував "notifyControllerEvent ()". В іншому випадку це закінчилося деякими помилками.
Vel Murugan S

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.

Додавання наступних директив лінійки робить атрибут HTML "автофокусування" ефективним на Angular.js.

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

Тепер ви можете просто використовувати синтаксис автоматичного фокусування HTML5, наприклад:

<input type="text" autofocus>

@Stephane так, так це стає.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 />

2

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

.directive('autofocus', [function () {
    return {
        require : 'ngModel',
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.focus();
        }
    };
}])

1
ви отримаєте помилку при виконанні цього завдання, якщо не включений jQuery, він замість цього повинен бути елементом [0] .focus ()
Ryan M

2

Якщо ви використовуєте modalInstance і маєте об'єкт, ви можете використовувати "тоді" для виконання дій після відкриття модального. Якщо ви не використовуєте modalInstance і жорстко закодовані для відкриття модалу, ви можете використовувати подію. $ Timeout не є хорошим рішенням.

Ви можете зробити (Bootstrap3):

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

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

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

Не використовуйте $ timeout, нехай метод підкаже, коли ви можете зосередитись;)

Я сподіваюся, що це допоможе! :)


2

Усі попередні відповіді не працюють, якщо бажаний елемент фокусування вводиться в шаблон директиви. Наступна директива підходить як до простого елемента, так і до введеного директивою елемента (я написав це в машинопис ). він приймає селектор для внутрішнього фокусується елемента. якщо вам просто потрібно зосередити самоелемент - не надсилайте жодних параметрів вибору до директиви:

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 Марка Rajcok для роботи для декількох фокусів в одному елементі.

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 або $ range. $ Apply () для запуску циклу дайвінгу. 4. Вхідний елемент присутній всередині елемента, де Директива використовується відкрито.

Це рішення, яке мені найбільше сподобалось:

Директива:

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

Я сподіваюся, що це допоможе комусь там!


Ви просто відтворили те, що робить 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-атрибути та атрибути класу, щоб вибрати потрібний елемент, і застосувати фокус (або еквіваленти jQuery) службі використання document.getElementById або document.querySelector.

Розмітка - це стандартні HTML / кутові директиви із доданими id / класами для вибору

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

Контролер транслює подію

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

У сервісі інтерфейсу використовується querySelector - якщо є кілька збігів (скажімо, через клас), він поверне лише перше

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

Ви можете використовувати $ timeout (), щоб примусити цикл дайвінгу


0

Просто кинувши трохи кави.

app.directive 'ngAltFocus', ->
    restrict: 'A'
    scope: ngAltFocus: '='
    link: (scope, el, attrs) ->
        scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.