Làm cách nào để đặt tiêu điểm vào trường đầu vào?


759

'Cách góc' để đặt tiêu điểm vào trường đầu vào trong AngularJS là gì?

Yêu cầu cụ thể hơn:

  1. Khi một Modal được mở, hãy tập trung vào một <input>Modal được xác định trước bên trong Modal này.
  2. Mọi lúc <input>sẽ hiển thị (ví dụ: bằng cách nhấp vào nút nào đó), hãy tập trung vào nút đó.

Tôi đã cố gắng đạt được yêu cầu đầu tiên với autofocus, nhưng điều này chỉ hoạt động khi Modal được mở lần đầu tiên và chỉ trong một số trình duyệt nhất định (ví dụ: trong Firefox nó không hoạt động).

Bất kỳ trợ giúp sẽ được đánh giá cao.

Câu trả lời:


579
  1. Khi một Modal được mở, hãy đặt tiêu điểm vào <input> được xác định trước bên trong Modal này.

Xác định một lệnh và yêu cầu $ xem thuộc tính / kích hoạt để nó biết khi nào cần tập trung phần tử:

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

Thời gian chờ $ dường như là cần thiết để cung cấp thời gian phương thức để kết xuất.

'2.' Mỗi khi <input> hiển thị (ví dụ: bằng cách nhấp vào nút nào đó), hãy đặt tiêu điểm vào nó.

Tạo một chỉ thị về cơ bản giống như ở trên. Xem một số thuộc tính phạm vi và khi nó trở thành đúng (đặt nó trong trình xử lý ng-click của bạn), hãy thực thi element[0].focus(). Tùy thuộc vào trường hợp sử dụng của bạn, bạn có thể hoặc không cần thời gian chờ $ cho việc này:

<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


Cập nhật 7/2013 : Tôi đã thấy một vài người sử dụng các chỉ thị phạm vi cô lập ban đầu của tôi và sau đó gặp sự cố với các trường đầu vào được nhúng (nghĩa là một trường đầu vào trong phương thức). Một chỉ thị không có phạm vi mới (hoặc có thể là phạm vi con mới) sẽ làm giảm bớt một số nỗi đau. Vì vậy, ở trên tôi đã cập nhật câu trả lời để không sử dụng phạm vi cô lập. Dưới đây là câu trả lời ban đầu:

Câu trả lời gốc cho 1., sử dụng phạm vi cô lập:

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 .

Câu trả lời gốc cho 2., sử dụng phạm vi cô lập:

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

Vì chúng ta cần đặt lại thuộc tính trigger / FocusInput trong lệnh, '=' được sử dụng cho cơ sở dữ liệu hai chiều. Trong chỉ thị đầu tiên, '@' là đủ. Cũng lưu ý rằng khi sử dụng '@', chúng tôi so sánh giá trị kích hoạt với "true" vì @ luôn dẫn đến một chuỗi.


1
Xem thêm Chỉ thị "tập trung" của @ Josh: stackoverflow.com/a/14859639/215945 Ông không sử dụng phạm vi cách ly trong triển khai.
Mark Rajcok

2
@MarkRajcok chỉ tò mò về điều này: phiên bản này hoạt động nhưng nếu tôi đặt một trường ng-modeltrên đầu vào, giá trị mô hình sẽ bị mất khi tôi sử dụng lệnh này với phạm vi cách ly được sử dụng. Vấn đề không xảy ra nếu tôi thử phiên bản của Josh mà không có phạm vi cô lập. Vẫn là một người mới và tôi muốn hiểu sự khác biệt. Đây là một Plunker cho thấy nó.
Sunil D.

1
Tôi thấy rằng # 1 hoạt động rất tốt với AngularJS 1.0.6. Tuy nhiên, khi chạy theo trình gỡ lỗi, tôi nhận thấy rằng mỗi lần tôi loại bỏ và mở lại phương thức của mình, tôi lại thấy một cuộc gọi bổ sung cho chức năng đặt tiêu điểm so với thời gian trước đó. Tôi đã sửa đổi chức năng đó một chút để hủy liên kết đồng hồ khi value != "true"và điều đó xuất hiện để giải quyết vấn đề của tôi.
Andrew Brown

1
Vì vậy, ... trên một trang tiểu bang, tôi cũng có một điều tương tự vì $ watch là $ watch và các nhà phát triển góc cạnh yêu $ watch ... tôi đã gặp phải một vấn đề, Mr @MarkRajcok, một vấn đề tôi đã giải quyết với giải pháp (cơ bản) tôi đề xuất dưới đây.
Ben Lesh

3
hmm đối với tôi mã hoạt động, vì trong tôi có thể thấy nó thực thi đúng và phần tử [0] là điều khiển đúng. tuy nhiên .f Focus () không kích hoạt tiêu điểm. có thể bootstrap hoặc cái gì khác đang can thiệp vào điều này?
Linh hồn Sonic

264

(EDIT: Tôi đã thêm một giải pháp cập nhật bên dưới phần giải thích này)

Mark Rajcok là người đàn ông ... và câu trả lời của anh ta là một câu trả lời hợp lệ, nhưng nó đã có một khiếm khuyết (xin lỗi Mark) ...

... Hãy thử sử dụng boolean để tập trung vào đầu vào, sau đó làm mờ đầu vào, sau đó thử sử dụng nó để tập trung lại đầu vào. Nó sẽ không hoạt động trừ khi bạn đặt lại boolean thành false, sau đó $ digest, sau đó đặt lại thành đúng. Ngay cả khi bạn sử dụng so sánh chuỗi trong biểu thức của mình, bạn sẽ buộc phải thay đổi chuỗi thành một thứ khác, $ digest, sau đó thay đổi lại chuỗi. (Điều này đã được giải quyết với trình xử lý sự kiện mờ.)

Vì vậy, tôi đề xuất giải pháp thay thế này:

Sử dụng một sự kiện, tính năng bị lãng quên của Angular.

JavaScript yêu tất cả các sự kiện. Các sự kiện vốn đã được kết nối lỏng lẻo và thậm chí tốt hơn, bạn tránh thêm một chiếc đồng hồ $ khác vào thông báo $ của bạn.

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

Vì vậy, bây giờ bạn có thể sử dụng nó như thế này:

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

và sau đó bất cứ nơi nào trong ứng dụng của bạn ...

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

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

Điều này thật tuyệt vời vì bạn có thể làm tất cả mọi thứ với những thứ như thế này. Đối với một, bạn có thể buộc vào các sự kiện đã tồn tại. Đối với một điều khác, bạn bắt đầu làm một cái gì đó thông minh bằng cách có các phần khác nhau trong ứng dụng của bạn xuất bản các sự kiện mà các phần khác trong ứng dụng của bạn có thể đăng ký.

Nhưng dù sao, loại điều này hét lên "sự kiện thúc đẩy" với tôi. Tôi nghĩ với tư cách là nhà phát triển Angular, chúng tôi rất cố gắng để đóng các chốt hình phạm vi $ vào các lỗ hình dạng sự kiện.

Đó có phải là giải pháp tốt nhất? Tôi không biết. Đó là một giải pháp.


Giải pháp cập nhật

Sau bình luận của @ ShimonRachlenko bên dưới, tôi đã thay đổi phương pháp làm việc này một chút. Bây giờ tôi sử dụng kết hợp dịch vụ và chỉ thị xử lý một sự kiện "đằng sau hậu trường":

Ngoài ra, đó là cùng một hiệu trưởng được nêu ở trên.

Dưới đây là bản demo nhanh Plunk

Sử dụng

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

Nguồn

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
Bạn cần phải bọc cuộc gọi đến $broadcastvới $timeoutnếu bạn muốn điều này để làm việc trên vào bộ điều khiển. Nếu không thì giải pháp tốt đẹp.
Shimon Rachlenko

@ShimonRachlenko - Cảm ơn. Nhưng tôi không chắc ý của bạn là gì khi hết thời gian chờ. Nếu tôi muốn phát khi trình xây dựng bộ điều khiển đang được xử lý, tôi sẽ chỉ phát ngay sau đó. Hết thời gian chờ sẽ không làm gì ngoài việc trì hoãn phát đến một lần thực hiện sau trong vòng lặp sự kiện.
Ben Lesh

1
Có, và thế là đủ để chỉ thị khởi tạo. Ngoài ra, sự kiện được phát trước khi lệnh bắt đầu lắng nghe .. Một lần nữa, điều này chỉ cần thiết khi bạn muốn kích hoạt lệnh của mình khi bạn vào trang.
Shimon Rachlenko

2
Bạn hoàn toàn đúng. Tôi đoán tôi đã không sử dụng nó để tập trung vào tải. Tôi sẽ cập nhật câu trả lời với một cái gì đó mạnh mẽ hơn.
Ben Lesh

1
Đây là, giải pháp "góc cạnh" thanh lịch nhất. Mặc dù tôi hầu như chỉ sao chép mã khi lần đầu tiên gặp vấn đề này, tôi rất vui vì bạn đã tạo một mô-đun cho nó! Tôi thành thật nghĩ rằng nó có thể đáng để cố gắng đưa nó vào góc cạnh cốt lõi.
Randolpho

241

Tôi đã tìm thấy một số câu trả lời khác quá phức tạp khi tất cả những gì bạn thực sự cần là

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

sử dụng là

<input name="theInput" auto-focus>

Chúng tôi sử dụng thời gian chờ để cho phép mọi thứ trong dom hiển thị, mặc dù nó bằng 0, nhưng ít nhất nó chờ đợi điều đó - theo cách này hoạt động theo phương thức và cũng không có gì


1
Sẽ có một số cách để làm điều này, một cách có thể thực sự đơn giản và dễ dàng là trên phạm vi (bộ điều khiển ở đây) đặt ID của mục mà bạn muốn tập trung vào khi bạn nhấp vào nút, sau đó vào Chỉ thị đơn giản là lắng nghe điều này. Trong trường hợp này, bạn sẽ không phải đặt lệnh ở bất cứ đâu, đặc biệt là ở đâu đó trong trang đó (tôi đã sử dụng các loại chỉ thị theo dõi này thành công trong quá khứ, đặc biệt là tập trung vào mọi thứ và cuộn mọi thứ) - Sau đó, nếu bạn ' đang sử dụng jquery (sẽ làm cho việc này đơn giản hơn) chỉ cần tìm id phần tử đó và tập trung vào nó
ecancil

14
Giải pháp với thời gian chờ bằng 0 không hoạt động đối với tôi nếu đầu vào được đặt trong phương thức bật lên. Nhưng thậm chí 10 ms đã khắc phục sự cố
Eugene Fidelin

@ecancil: Tôi thích cách tiếp cận của bạn vì nó đơn giản nhất, nhưng bạn cần đặt thời gian chờ là ~ 500ms trong IE vì hoạt ảnh của giao diện phương thức dẫn đến một con trỏ nhấp nháy bên ngoài đầu vào. Tôi không biết một cách hay để điều này xảy ra khi hoạt hình kết thúc, vì vậy tôi vũ phu buộc nó với 500ms.
Dev93

sẽ không hoạt động, nếu bạn phải lấy thêm dữ liệu từ cơ sở dữ liệu, nó cần đợi bộ điều khiển hoàn thành việc kéo dữ liệu, vì vậy hãy thêm đủ thời gian thay cho 0
Ali Adravi

1
Đối với những người như @Ade, những người muốn sử dụng điều này một cách đơn giản ng-click: Giả sử nhấp vào một nút có ng-click="showInput = !showInputtrên đầu vào của bạn. Sau đó, trên đầu vào thực tế của bạn, thêm vào ng-if="showInput". Nhấn nút sẽ làm cho lệnh chạy lại mỗi lần. Tôi đã có một vấn đề với điều này khi tôi đang sử dụng ng-showđó là cách tiếp cận sai.
JVG

91

HTML có một thuộc tính autofocus.

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

http://www.w3schools.com/tags/att_input_autof Focus.asp


29
Thật không may, điều này chỉ hoạt động một lần nếu có. Tôi đã sử dụng điều này trong một chỉnh sửa Angular trong tình huống tại chỗ và nó đã hoạt động rất tốt ngay lần đầu tiên trong Chrome, hoàn toàn không phải trong Firefox. Trong Chrome, khi tôi nhấp vào một mục khác để chỉnh sửa tại chỗ hoặc thậm chí trên cùng một mục một lần nữa, nó sẽ không còn tập trung nữa. Các tài liệu nói rằng nó hoạt động khi tải trang, điều này chỉ xảy ra một lần trong ứng dụng Angular. Nếu chỉ đơn giản như vậy, tất cả chúng ta sẽ ngồi trên một bãi biển kiếm 20%!
Nocturno

62

Bạn cũng có thể sử dụng chức năng jqlite được tích hợp trong góc.

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


2
Không tải jquery: angular.forEach (document.querySelector ALL ('. Selector'), function (elem) {elem.f Focus ();});
Dan Benamy

29
Nó không phải là một thực hành xấu để đặt nó vào một bộ điều khiển?
VitalyB

2
Nếu việc đặt dòng jqlite này bên trong bộ điều khiển là một thực tiễn tồi, thì tốt hơn là đặt dòng jqlite này trong một lệnh?
thể thao

3
Tôi nhận được điều này:Looking up elements via selectors is not supported by jqLite!
Maria Ines Parnisari

1
@VitalyB Tôi đã có một trường hợp tôi cần làm mờ một phần tử sau một cuộc gọi ajax. Tôi có thể thêm chỉ một dòng JS thuần trong cuộc gọi lại bên trong bộ điều khiển hoặc xây dựng toàn bộ chỉ thị dành cho độ mờ của đầu vào đó. Tôi chỉ cảm thấy nó quá rắc rối, vì vậy tôi đã đi đến lựa chọn đầu tiên.
Sebastianb

55

Điều này hoạt động tốt và một cách góc cạnh để tập trung kiểm soát đầu vào

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

Điều này mặc dù không phải là một cách thuần túy để thực hiện nhiệm vụ nhưng cú pháp theo kiểu góc cạnh. Jquery đóng vai trò gián tiếp và trực tiếp truy cập DOM bằng cách sử dụng Angular (jQLite => JQuery Light).

Nếu được yêu cầu, mã này có thể dễ dàng được đặt bên trong một chỉ thị góc đơn giản, nơi phần tử có thể truy cập trực tiếp.


Tôi không chắc đó là một cách tiếp cận tuyệt vời cho các ứng dụng ViewModel. Trong Angular nó phải được thực hiện thông qua các chỉ thị.
Agat

ví dụ: Nếu ai đó thay đổi texbox A và bạn cần hiển thị bật lên và đặt tiêu điểm vào hộp văn bản khác B.
Sanjeev Singh

1
Tôi gặp lỗi này khi sử dụng:Looking up elements via selectors is not supported by jqLite!
JVG

6
Theo như tôi có thể nói, trước tiên bạn cần tải jQuery làm phụ thuộc, trong trường hợp đó angular.elementtrở thành trình bao bọc cho $() / jQuery(). Vì vậy, nếu không điều này sẽ không hoạt động và về cơ bản bạn chỉ đang sử dụng jQuery (nhưng hãy sửa tôi nếu tôi sai)
JVG

@Jascination: jqLite được phát triển để loại bỏ phụ thuộc jQuery. Bạn không cần toàn bộ jQuery để truy cập một phần tử trong DOM. Nhưng nếu bạn đã cài đặt jQuery, thì Angular sẽ giới thiệu jQuery. Kiểm tra điều này: docs.angularjs.org/api/angular.element
Sanjeev Singh

31

Tôi không nghĩ $ timeout là một cách tốt để tập trung vào yếu tố sáng tạo. Đây là một phương pháp sử dụng chức năng góc tích hợp, đào ra từ độ sâu âm u của các tài liệu góc. Lưu ý cách thuộc tính "liên kết" có thể được chia thành "trước" và "bài", cho các chức năng liên kết trước và sau liên kết.

Ví dụ hoạt động: 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 />

Tài liệu chỉ thị đầy đủ của AngularJS: https://docs.angularjs.org/api/ng/service/$compile


2
Phải bọc phần tử [0] .f Focus () trong thời gian chờ $ để làm cho nó hoạt động với tôi.
codin

@bbodenmiller Phương thức của tôi có một thứ mờ dần được áp dụng, khi phần tử được xây dựng, nó là vô hình (trong suốt 100%), do đó, trình duyệt chặn tập trung vào lời gọi tập trung. Rõ ràng một số mili giây trôi qua cho phép tập trung vào đầu vào / nút gần như trong suốt nhưng có thể nhìn thấy.
snowindy 8/10/2015

1
theo các tài liệu, các chức năng "liên kết" là "postLink" theo mặc định. xem thêm: bennadel.com/blog/ Kẻ
jody tate

17

Đây là giải pháp ban đầu của tôi:

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

Và HTML:

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

Những gì nó làm:

Nó tập trung đầu vào khi nó hiển thị với ng-show. Không sử dụng $ watch hoặc $ trên đây.


Trên thực tế tôi tin rằng {{isVisible}} dù sao cũng đang tạo ra một chiếc đồng hồ, vì vậy câu lệnh "Không sử dụng $ watch" là không chính xác.
masimplo

Vâng, tôi nghĩ bạn đúng. Nhưng nó vẫn phục vụ như một cách dễ dàng để làm điều đó.
Edhowler

17

Tôi đã viết một chỉ thị tập trung ràng buộc hai chiều, giống như mô hình gần đây.

Bạn có thể sử dụng chỉ thị tập trung như thế này:

<input focus="someFocusVariable">

Nếu bạn thực hiện một số biến phạm viF FocusVariable trueở bất kỳ đâu trong bộ điều khiển của bạn, đầu vào sẽ được tập trung. Và nếu bạn muốn "làm mờ" đầu vào của mình thì một sốF FocusVariable có thể được đặt thành false. Nó giống như câu trả lời đầu tiên của Mark Rajcok nhưng có ràng buộc hai chiều.

Đây là chỉ thị:

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

Sử dụng:

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

Đây là câu đố:

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


Đây phải là câu trả lời chính xác. Nó bao gồm tất cả các trường hợp sử dụng.
Kunal

11

Đối với những người sử dụng Angular với plugin Bootstrap:

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

Bạn có thể thực hiện openedlời hứa của thể hiện:

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

modalInstance.result.then(function ( etc...

Tốt Tuy nhiên trong trường hợp của tôi, $timeoutvới 50mslà cần thay vì 0.
lk_vc

8

Tôi thấy nó hữu ích để sử dụng một biểu thức chung. Bằng cách này, bạn có thể thực hiện những việc như tự động di chuyển tiêu điểm khi văn bản đầu vào hợp lệ

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

Hoặc tự động lấy nét khi người dùng hoàn thành trường có độ dài cố định

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

Và tất nhiên tập trung sau khi tải

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

Mã cho chỉ thị:

.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

Không hồi sinh zombie hoặc cắm lệnh của riêng tôi (ok đó chính xác là những gì tôi đang làm):

https://github.com/hiebj/ng-f 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
        };
    }
})();

Bây giờ tôi đang sử dụng chỉ thị này và nó hoạt động rất tốt, giải quyết vấn đề chung và không quá phức tạp. Đưa ra tìm kiếm một giải pháp $ timeout-free. Tiếp tục tìm các bình luận nói rằng trọng tâm là "trên lộ trình" cho Angular 1.1 và 1.2, nhưng tôi đang sử dụng 2.x và vẫn không có quản lý tập trung. Heh.
tekHedd

6

Đầu tiên, một cách chính thức để tập trung là vào lộ trình cho 1.1 . Trong khi đó, bạn có thể viết một lệnh để thực hiện cài đặt trọng tâm.

Thứ hai, để tập trung vào một mục sau khi nó trở nên hữu hình hiện cần một cách giải quyết. Chỉ cần trì hoãn cuộc gọi của bạn đến phần tử tập trung () với a $timeout.

Bởi vì cùng một vấn đề điều khiển-sửa đổi-DOM tồn tại để lấy nét, làm mờ và chọn, tôi đề nghị có một lệnh 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>

Chủ đề góc ở đây: http://goo.gl/ipsx4 , và nhiều chi tiết khác được viết ở đây: http://goo.gl/4rdZa

Lệnh sau sẽ tạo một .focus()hàm bên trong bộ điều khiển của bạn như được chỉ định bởi ng-targetthuộc tính của bạn . (Nó tạo ra một .blur().select()quá.) Demo: http://jsfiddle.net/bseib/WUcQX/


+1 cho tham chiếu lộ trình. Trong thực tế, tôi chỉ thấy nó trong tài liệu của 1.2 vẫn được coi là không ổn định ( docs.angularjs.org/api/ng.directive:ngF Focus )
Edwin Dalorzo

27
@EdwinDalorzo ngFocusdường như là một cách để xử lý focuscác sự kiện, không phải là một cách để thiết lập tập trung vào một phần tử.
teleclimber

6

Thay vì tạo chỉ thị của riêng bạn, có thể chỉ cần sử dụng các hàm javascript để thực hiện một tiêu điểm.

Đây là một ví dụ.

Trong tệp html:

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

Trong một tệp javascript, ví dụ trong bộ điều khiển, nơi bạn muốn kích hoạt tiêu điểm:

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

9
Đây không phải là một "cách góc cạnh" và đừng khuyên mọi người chạm vào DOM trong bộ điều khiển của họ.
smajl

Tốt nhất không sử dụng vani trong Angular, vì Angular không thể theo dõi nó cũng như không giữ nó đồng bộ với mọi thứ khác.
Edgar Quintero

4

Nếu bạn chỉ muốn một tiêu điểm đơn giản được điều khiển bằng ng-click.

Html:

<input ut-focus="focusTigger">

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

Chỉ thị:

'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

Một cái đơn giản hoạt động tốt với các phương thức:

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

        link: function (scope, element, attrs)
        {


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



        }
    };
}])

Thí dụ

<input ng-model="your.value" focus-me-now />

Hoàn hảo cho tôi. Cảm ơn bạn
Alexander

3

Bạn chỉ có thể tạo một lệnh bắt buộc tập trung vào phần tử được trang trí trên postLinking:

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

Sau đó, trong html của bạn:

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

Điều này sẽ hoạt động cho các phương thức và các yếu tố ng-if được bật, không phải cho ng-show vì postLinking chỉ xảy ra khi xử lý HTML.


3

Mark và B Meat có câu trả lời tuyệt vời; tuy nhiên, Mark's có một lỗ hổng mà B Meat chỉ ra (ngoài việc phức tạp để thực hiện) và tôi cảm thấy rằng câu trả lời của B Meat có lỗi ngữ nghĩa trong việc tạo ra một dịch vụ đặc biệt là gửi yêu cầu tập trung đến frontend khi thực sự tất cả những gì anh ta cần là một cách để trì hoãn sự kiện cho đến khi tất cả các chỉ thị được lắng nghe.

Vì vậy, đây là những gì tôi đã làm, đánh cắp rất nhiều từ câu trả lời của B Meat nhưng giữ cho ngữ nghĩa của sự kiện điều khiển và dịch vụ "sau khi tải" tách biệt.

Điều này cho phép sự kiện của bộ điều khiển dễ dàng được nối với những thứ khác ngoài việc chỉ tập trung vào một yếu tố cụ thể và cũng chỉ cho phép phát sinh chức năng "sau khi tải" nếu cần, trong nhiều trường hợp có thể không cần thiết.

Sử dụng

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

  afterLoad(notifyControllerEvent);
});

Nguồn

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

Câu trả lời duy nhất và duy nhất mà tôi (một người mới bắt đầu hoàn thành) có thể hiểu. Thay vì "afterLoad (notifyControllEvent);", tôi đã sử dụng để "notifyControllEvent ()". Nếu không, nó đã kết thúc với một số lỗi.
Vel Murugan S

3

Điều này cũng có thể sử dụng ngModelController . Làm việc với 1.6+ (không biết với các phiên bản cũ hơn).

HTML

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

Mã não

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

-

Lưu ý: Tùy thuộc vào ngữ cảnh, bạn có thể phải sử dụng chức năng hết thời gian chờ.

NB²: Khi sử dụng controllerAs, điều này gần như giống nhau. Chỉ cần thay thế name="myForm"bằng name="vm.myForm"và trong JS , vm.myForm.myText.$$element.focus();.


3

Có lẽ, giải pháp đơn giản nhất trong độ tuổi ES6.

Việc thêm một chỉ thị lót giúp cho thuộc tính 'tự động lấy nét' của HTML có hiệu lực trên Angular.js.

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

Bây giờ, bạn chỉ có thể sử dụng cú pháp tự động lấy nét HTML5 như:

<input type="text" autofocus>

@Stephane có, vậy thì nó sẽ trở thành.directive('autofocus', ['$timeout', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())})])
Michael Plautz

2

Chỉ là một người mới ở đây, nhưng tôi đã sẵn sàng làm cho nó hoạt động trong một ui.bootstrap.modal với chỉ thị này:

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

và trong phương thức $ modal.open, tôi đã sử dụng folowing để chỉ ra phần tử cần lấy nét:

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

trên mẫu tôi có cái này:

<input id="myInputId" focus />

2

Các chỉ thị sau đây đã lừa tôi. Sử dụng thuộc tính html tự động lấy nét tương tự cho đầu vào.

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

1
bạn sẽ gặp lỗi khi chạy lỗi này trừ khi có jQuery, thay vào đó phải là phần tử [0] .f Focus ()
Ryan M

2

Nếu bạn đang sử dụng modalInstance và có đối tượng bạn có thể sử dụng "thì" để thực hiện các hành động sau khi mở phương thức. Nếu bạn không sử dụng modalInstance và mã hóa cứng để mở phương thức, bạn có thể sử dụng sự kiện. Thời gian chờ $ không phải là một giải pháp tốt.

Bạn có thể làm (Bootstrap3):

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

Tại modalInstance, bạn có thể xem thư viện để biết cách thực thi mã sau khi mở phương thức.

Không sử dụng $ timeout như thế này, thời gian chờ $ có thể là 0, 1, 10, 30, 50, 200 hoặc nhiều hơn tùy thuộc vào máy tính của khách hàng và quá trình mở chế độ.

Đừng sử dụng $ timeout để phương thức cho bạn biết khi nào bạn có thể tập trung;)

Tôi hy vọng rằng điều này sẽ giúp! :)


2

Tất cả các câu trả lời trước không hoạt động nếu phần tử tiêu điểm mong muốn được đưa vào một mẫu chỉ thị. Lệnh sau phù hợp với cả phần tử đơn giản hoặc phần tử được chỉ thị (tôi đã viết nó trong bản in ). nó chấp nhận bộ chọn cho phần tử có thể tập trung bên trong. nếu bạn chỉ cần tập trung vào phần tử tự - không gửi bất kỳ tham số bộ chọn nào cho lệnh:

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

}

ví dụ sử dụng cho phần tử đơn giản:

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

ví dụ sử dụng cho phần tử bên trong (thường cho phần tử được chèn động như chỉ thị với mẫu):

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

bạn có thể sử dụng bất kỳ bộ chọn jQuery nào thay vì "input"


2

Tôi chỉnh sửa chỉ thị FocusMe của Mark Rajcok để hoạt động cho nhiều tiêu điểm trong một yếu tố.

HTML:

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

trong Bộ điều khiển AngularJs:

$scope.myInputFocus= true;

Chỉ thị 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

Tôi muốn đóng góp cho cuộc thảo luận này sau khi tìm kiếm giải pháp tốt hơn và không tìm thấy nó, thay vào đó phải tạo ra nó.

Tiêu chí: 1. Giải pháp nên độc lập với phạm vi điều khiển chính để tăng khả năng sử dụng lại. 2. Tránh sử dụng $ watch để theo dõi một số điều kiện, điều này vừa chậm, làm tăng kích thước của vòng lặp digest và làm cho việc kiểm tra khó hơn. 3. Tránh $ timeout hoặc $ scope. $ Áp dụng () để kích hoạt vòng lặp digest. 4. Một phần tử đầu vào có mặt trong phần tử mà Chỉ thị được sử dụng mở.

Đây là giải pháp tôi thích nhất:

Chỉ thị:

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

Tôi hy vọng điều này sẽ giúp ai đó ngoài kia!


Bạn chỉ cần tạo lại "nhãn" html, bạn chỉ có thể thay thế "div tập trung-đầu vào" bằng "nhãn" và thoát khỏi chỉ thị.
frst

1

Thật dễ dàng .. hãy thử điều này

html

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

javascript

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

Điều này đúng trong bối cảnh, nhưng không phải là cách làm của AngularJS
CodeMonkey

1

Nếu bạn muốn tập trung vào yếu tố cụ thể, bạn có thể sử dụng cách tiếp cận bên dưới.

  1. Tạo một dịch vụ gọi là 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. Tiêm nó vào bộ điều khiển từ nơi bạn muốn gọi.

  3. Gọi dịch vụ này.


0

Tôi nghĩ rằng chỉ thị là không cần thiết. Sử dụng các thuộc tính id và lớp HTML để chọn thành phần bắt buộc và sử dụng dịch vụ document.getEuityById hoặc document.querySelector để áp dụng tiêu điểm (hoặc tương đương jQuery).

Đánh dấu là các chỉ thị HTML / góc tiêu chuẩn có thêm id / lớp để lựa chọn

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

Điều khiển phát sóng sự kiện

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

Trong dịch vụ UI sử dụng querySelector - nếu có nhiều kết quả khớp (giả sử là do lớp), nó sẽ chỉ trả về cái đầu tiên

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

Bạn có thể muốn sử dụng $ timeout () để buộc chu trình tiêu hóa


0

Chỉ cần ném vào một số cà phê.

app.directive 'ngAltFocus', ->
    restrict: 'A'
    scope: ngAltFocus: '='
    link: (scope, el, attrs) ->
        scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.