Đặt tiêu điểm phần tử theo cách góc cạnh


113

Sau khi tìm kiếm các ví dụ về cách đặt các yếu tố lấy nét bằng góc, tôi thấy rằng hầu hết trong số họ sử dụng một số biến để theo dõi sau đó đặt tiêu điểm và hầu hết trong số họ sử dụng một biến khác nhau cho mỗi trường mà họ muốn đặt tiêu điểm. Trong một biểu mẫu, với nhiều trường, có nghĩa là rất nhiều biến khác nhau.

Với ý nghĩ về jquery, nhưng muốn làm điều đó theo cách góc cạnh, tôi đã đưa ra giải pháp là chúng tôi đặt trọng tâm vào bất kỳ hàm nào bằng cách sử dụng id của phần tử, vì vậy, vì tôi còn rất mới về góc cạnh, tôi muốn có một số ý kiến ​​nếu cách đó là đúng, có vấn đề, bất cứ điều gì, bất cứ điều gì có thể giúp tôi làm điều này theo cách tốt hơn về góc độ.

Về cơ bản, tôi tạo một chỉ thị theo dõi giá trị phạm vi do người dùng xác định bằng chỉ thị hoặc focusElement của mặc định và khi giá trị đó giống với id của phần tử, phần tử đó sẽ tự đặt tiêu điểm.

angular.module('appnamehere')
  .directive('myFocus', function () {
    return {
      restrict: 'A',
      link: function postLink(scope, element, attrs) {
        if (attrs.myFocus == "") {
          attrs.myFocus = "focusElement";
        }
        scope.$watch(attrs.myFocus, function(value) {
          if(value == attrs.id) {
            element[0].focus();
          }
        });
        element.on("blur", function() {
          scope[attrs.myFocus] = "";
          scope.$apply();
        })        
      }
    };
  });

Một đầu vào cần tập trung vào một số lý do, sẽ làm theo cách này

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

Đây là bất kỳ phần tử nào để đặt tiêu điểm:

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

Và ví dụ về chức năng đặt tiêu điểm:

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

Đó có phải là một giải pháp tốt trong góc cạnh? Nó có vấn đề mà với kinh nghiệm kém của tôi mà tôi chưa thấy?

Câu trả lời:


173

Vấn đề với giải pháp của bạn là nó không hoạt động tốt khi bị ràng buộc với các chỉ thị khác tạo ra một phạm vi mới, ví dụ ng-repeat. Một giải pháp tốt hơn là chỉ cần tạo một hàm dịch vụ cho phép bạn tập trung các phần tử một cách ẩn ý trong bộ điều khiển của mình hoặc tập trung các phần tử một cách khai báo trong html.

BẢN GIỚI THIỆU

JAVASCRIPT

Dịch vụ

 .factory('focus', function($timeout, $window) {
    return function(id) {
      // timeout makes sure that it is invoked after any other event has been triggered.
      // e.g. click events that need to run before the focus or
      // inputs elements that are in a disabled state but are enabled when those events
      // are triggered.
      $timeout(function() {
        var element = $window.document.getElementById(id);
        if(element)
          element.focus();
      });
    };
  });

Chỉ thị

  .directive('eventFocus', function(focus) {
    return function(scope, elem, attr) {
      elem.on(attr.eventFocus, function() {
        focus(attr.eventFocusId);
      });

      // Removes bound events in the element itself
      // when the scope is destroyed
      scope.$on('$destroy', function() {
        elem.off(attr.eventFocus);
      });
    };
  });

Bộ điều khiển

.controller('Ctrl', function($scope, focus) {
    $scope.doSomething = function() {
      // do something awesome
      focus('email');
    };
  });

HTML

<input type="email" id="email" class="form-control">
<button event-focus="click" event-focus-id="email">Declarative Focus</button>
<button ng-click="doSomething()">Imperative Focus</button>

Tôi thực sự thích giải pháp này. Tuy nhiên, bạn có thể giải thích lý do sử dụng $ timeout nhiều hơn một chút không? Lý do bạn sử dụng nó là do "Angular Thing" hoặc "DOM Thing"?
user1821052

Nó đảm bảo rằng nó chạy sau bất kỳ chu kỳ thông báo nào mà góc thực hiện, nhưng điều này loại trừ các chu kỳ thông báo bị ảnh hưởng sau một hành động không đồng bộ được thực thi sau thời gian chờ.
ryeballar

3
Cảm ơn! Đối với những người tự hỏi, nơi này được tham chiếu trong tài liệu góc, đây là liên kết (mất tôi mãi mãi để tìm)
user1821052

@ryeballar, Cảm ơn !. Giải pháp đơn giản tốt đẹp. Chỉ là một câu hỏi. Tôi có thể sử dụng nhà máy được tạo thông qua thuộc tính thay vì đợi sự kiện nào đó xảy ra không?
Pratik Gaikwad

4
Thật điên rồ khi khối lượng công việc cần thiết chỉ để tập trung đầu vào.
Bruno Santos

19

Về giải pháp này, chúng ta chỉ có thể tạo một chỉ thị và đính kèm nó vào phần tử DOM để lấy tiêu điểm khi một điều kiện nhất định được thỏa mãn. Bằng cách làm theo phương pháp này, chúng tôi tránh việc ghép bộ điều khiển với ID phần tử DOM.

Chỉ thị mã mẫu:

gbndirectives.directive('focusOnCondition', ['$timeout',
    function ($timeout) {
        var checkDirectivePrerequisites = function (attrs) {
          if (!attrs.focusOnCondition && attrs.focusOnCondition != "") {
                throw "FocusOnCondition missing attribute to evaluate";
          }
        }

        return {            
            restrict: "A",
            link: function (scope, element, attrs, ctrls) {
                checkDirectivePrerequisites(attrs);

                scope.$watch(attrs.focusOnCondition, function (currentValue, lastValue) {
                    if(currentValue == true) {
                        $timeout(function () {                                                
                            element.focus();
                        });
                    }
                });
            }
        };
    }
]);

Một cách sử dụng có thể

.controller('Ctrl', function($scope) {
   $scope.myCondition = false;
   // you can just add this to a radiobutton click value
   // or just watch for a value to change...
   $scope.doSomething = function(newMyConditionValue) {
       // do something awesome
       $scope.myCondition = newMyConditionValue;
  };

});

HTML

<input focus-on-condition="myCondition">

1
điều gì sẽ xảy ra khi biến myCondition$ scope đã được đặt thành true và sau đó người dùng chọn tập trung vào phần tử khác, bạn vẫn có thể kích hoạt lại tiêu điểm khi myConditionđã đúng, mã của bạn xem các thay đổi cho thuộc tính focusOnConditionnhưng nó sẽ không kích hoạt khi giá trị bạn cố gắng thay đổi vẫn không đổi.
ryeballar

Tôi sẽ cập nhật mẫu, trong trường hợp của chúng tôi, chúng tôi có hai nút radio, và chúng tôi chuyển cờ đúng hay sai tùy thuộc vào giá trị, bạn chỉ có thể thay đổi cờ myCondition đến đúng hay sai
Braulio

Có vẻ như một giải pháp chung chung. Tốt hơn là phụ thuộc vào id. Tôi thích nó.
mortb

Trong trường hợp bất kỳ ai khác thử điều này và nó không hoạt động, tôi đã phải thay đổi element.focus (); thành phần tử [0] .focus ();
Adrian Carr

1
Giải pháp này là 'cách góc cạnh' hơn nhiều so với hack dựa trên id ở trên.
setec

11

Tôi muốn tránh tra cứu DOM, đồng hồ và bộ phát toàn cầu bất cứ khi nào có thể, vì vậy tôi sử dụng cách tiếp cận trực tiếp hơn. Sử dụng một chỉ thị để gán một chức năng đơn giản tập trung vào phần tử chỉ thị. Sau đó, gọi hàm đó ở bất cứ nơi nào cần thiết trong phạm vi của bộ điều khiển.

Đây là một cách tiếp cận đơn giản để gắn nó vào phạm vi. Xem đoạn mã đầy đủ để xử lý cú pháp controller-as.

Chỉ thị:

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

và trong html:

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

hoặc trong bộ điều khiển:

$scope.focusOnSaveInput();

Đã chỉnh sửa để cung cấp thêm giải thích về lý do cho cách tiếp cận này và để mở rộng đoạn mã để sử dụng như bộ điều khiển.


Điều đó rất tốt, và đã làm việc tốt cho tôi. Nhưng bây giờ tôi có một bộ đầu vào đang sử dụng ng-repeatvà tôi chỉ muốn đặt chức năng lấy nét cho cái đầu tiên. Bất kỳ ý tưởng nào về cách tôi có thể đặt một hàm tiêu điểm có điều kiện cho ví dụ <input>dựa trên $index?
Garret Wilson

Rất vui vì nó hữu ích. Góc 1 của tôi hơi bị gỉ, nhưng bạn có thể thêm một thuộc tính khác vào đầu vào, chẳng hạn như assign-focus-function-if="{{$index===0}}", sau đó là dòng đầu tiên của lối ra lệnh sớm trước khi gán một hàm nếu điều đó không đúng: if (attr.assignFocusFunctionIf===false) return; Lưu ý rằng tôi đang kiểm tra nếu nó rõ ràng falsevà không chỉ falsey nên chỉ thị sẽ vẫn hoạt động nếu thuộc tính đó không được xác định.
cstricklan

Controller-as đơn giản hơn nhiều với lodash. _.set(scope, attributes.focusOnSaveInput, function() { element.focus(); }).
Atomosk 20/09/18

9

Bạn co thể thử

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

ví dụ.

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

nó làm việc cho tôi.


4
Lưu ý: Điều này sẽ chỉ hoạt động nếu sử dụng jQuery đầy đủ thay vì dựa vào jqLite được nhúng trong Angular. Xem docs.angularjs.org/api/ng/osystem/angular.element
John Rix

4
Đây là cách jQuery để làm điều này, không phải là một cách góc cạnh. Câu hỏi đặc biệt yêu cầu làm thế nào để làm điều đó một cách góc cạnh.
forgivenson

4

Một tùy chọn khác sẽ là sử dụng kiến ​​trúc pub-sub có sẵn của Angular để thông báo chỉ thị của bạn để tập trung. Tương tự như các cách tiếp cận khác, nhưng sau đó nó không liên kết trực tiếp với một thuộc tính mà thay vào đó là lắng nghe phạm vi của nó cho một khóa cụ thể.

Chỉ thị:

angular.module("app").directive("focusOn", function($timeout) {
  return {
    restrict: "A",
    link: function(scope, element, attrs) {
      scope.$on(attrs.focusOn, function(e) {
        $timeout((function() {
          element[0].focus();
        }), 10);
      });
    }
  };
});

HTML:

<input type="text" name="text_input" ng-model="ctrl.model" focus-on="focusTextInput" />

Bộ điều khiển:

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

3

Tôi thích sử dụng một biểu thức. Điều này cho phép tôi làm những việc như tập trung vào một nút khi một trường hợp lệ, đạt đến độ dài nhất định và tất nhiên là sau khi tải.

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

Trên một hình thức phức tạp, điều này cũng làm giảm nhu cầu tạo các biến phạm vi bổ sung cho các mục đích tập trung.

Xem https://stackoverflow.com/a/29963695/937997

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.