Tetapkan fokus elemen dengan cara bersudut

113

Setelah mencari contoh bagaimana mengatur fokus elemen dengan sudut, saya melihat bahwa kebanyakan dari mereka menggunakan beberapa variabel untuk diperhatikan kemudian mengatur fokus, dan kebanyakan dari mereka menggunakan satu variabel berbeda untuk setiap bidang yang ingin mereka tetapkan fokus. Dalam bentuk, dengan banyak bidang, yang menyiratkan banyak variabel berbeda.

Dengan cara jquery dalam pikiran, tetapi ingin melakukan itu dengan cara sudut, saya membuat solusi yang kami tetapkan fokus dalam fungsi apa pun menggunakan id elemen, jadi, karena saya sangat baru dalam sudut, saya ingin mendapatkan beberapa pendapat jika jalan itu benar, punya masalah, apa pun, apa pun yang bisa membantu saya melakukan ini dengan cara yang lebih baik secara bersudut.

Pada dasarnya, saya membuat direktif yang melihat nilai cakupan yang ditentukan oleh pengguna dengan direktif, atau fokusElement default, dan ketika nilai itu sama dengan id elemen, elemen itu mengatur fokus itu sendiri.

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

Masukan yang perlu mendapatkan fokus karena alasan tertentu, akan dilakukan dengan cara ini

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

Di sini setiap elemen untuk mengatur fokus:

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

Dan contoh fungsi yang mengatur fokus:

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

Apakah itu solusi sudut yang bagus? Apakah ada masalah yang dengan pengalaman buruk saya, saya belum melihatnya?

Tiago Zortéa De Conto
sumber

Jawaban:

173

Masalah dengan solusi Anda adalah bahwa solusi tersebut tidak berfungsi dengan baik ketika terikat pada arahan lain yang menciptakan ruang lingkup baru, misalnya ng-repeat. Solusi yang lebih baik adalah dengan membuat fungsi layanan yang memungkinkan Anda untuk memfokuskan elemen secara imperatif dalam pengontrol Anda atau untuk memfokuskan elemen secara deklaratif di html.

DEMO

JAVASCRIPT

Layanan

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

Pengarahan

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

Kontroler

.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>
ryeballar
sumber
Saya sangat menyukai solusi ini. Bisakah Anda menjelaskan, bagaimanapun, alasan menggunakan $ timeout lebih banyak? Apakah alasan Anda menggunakannya karena "Angular Thing" atau "DOM Thing"?
pengguna1821052
Itu memastikan bahwa ia berjalan setelah siklus intisari apa pun yang dilakukan angular, tetapi ini mengecualikan siklus intisari yang terpengaruh setelah tindakan asinkron yang dijalankan setelah waktu tunggu.
ryeballar
3
Terima kasih! Bagi mereka yang bertanya-tanya di mana ini direferensikan dalam dokumen sudut, berikut tautannya (butuh waktu lama untuk saya temukan)
user1821052
@ryeballar, Terima kasih !. Solusi sederhana yang bagus. Hanya sebuah pertanyaan. Dapatkah saya menggunakan pabrik yang dibuat melalui atribut daripada menunggu beberapa peristiwa terjadi?
Pratik Gaikwad
4
Sungguh gila jumlah pekerjaan yang diperlukan dalam sudut hanya untuk memfokuskan input.
Bruno Santos
19

Tentang solusi ini, kita bisa membuat arahan dan melampirkannya ke elemen DOM yang harus mendapatkan fokus ketika kondisi tertentu terpenuhi. Dengan mengikuti pendekatan ini kami menghindari penggandengan pengontrol ke ID elemen DOM.

Contoh kode direktif:

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

Penggunaan yang memungkinkan

.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">
Braulio
sumber
1
apa yang akan terjadi jika myConditionvariabel $ scope telah disetel ke true dan kemudian pengguna memilih untuk fokus ke elemen lain, dapatkah Anda tetap memicu kembali fokus ketika myConditionsudah benar, kode Anda melihat perubahan untuk atribut focusOnConditiontetapi tidak akan memicu ketika nilai yang Anda coba ubah masih sama.
ryeballar
Saya akan memperbarui sampel, dalam kasus kami, kami memiliki dua tombol radio, dan kami mengubah bendera menjadi benar atau salah tergantung pada nilainya, Anda bisa mengubah bendera myCondition menjadi benar atau salah
Braulio
Sepertinya solusi umum. Lebih baik daripada bergantung pada id. Saya suka itu.
mortb
Jika ada orang lain yang mencoba ini dan tidak berhasil, saya harus mengubah element.focus (); ke elemen [0] .focus ();
Adrian Carr
1
Solusi ini jauh lebih 'sudut' daripada peretasan berbasis id di atas.
setec
11

Saya suka menghindari pencarian DOM, jam tangan, dan pemancar global jika memungkinkan, jadi saya menggunakan pendekatan yang lebih langsung. Gunakan direktif untuk menetapkan fungsi sederhana yang berfokus pada elemen direktif. Kemudian panggil fungsi itu di mana pun diperlukan dalam lingkup pengontrol.

Berikut adalah pendekatan yang disederhanakan untuk melampirkannya ke ruang lingkup. Lihat cuplikan lengkap untuk menangani sintaks controller-as.

Pengarahan:

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

dan dalam html:

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

atau di pengontrol:

$scope.focusOnSaveInput();

Diedit untuk memberikan penjelasan selengkapnya tentang alasan pendekatan ini dan untuk memperluas cuplikan kode untuk penggunaan controller-as.

cstricklan
sumber
Itu sangat bagus, dan telah bekerja dengan baik untuk saya. Tapi sekarang saya memiliki satu set input yang digunakan ng-repeat, dan saya hanya ingin mengatur fungsi fokus untuk yang pertama. Adakah ide bagaimana saya bisa secara kondisional mengatur fungsi fokus <input>berdasarkan $indexmisalnya?
Garret Wilson
Senang itu berguna. Sudut 1 saya sedikit berkarat, tetapi Anda harus dapat menambahkan atribut lain ke input, seperti assign-focus-function-if="{{$index===0}}", dan kemudian sebagai baris pertama dari perintah keluar lebih awal sebelum menetapkan fungsi jika itu tidak benar: if (attr.assignFocusFunctionIf===false) return; Catatan Saya memeriksa apakah itu secara eksplisit falsedan bukan hanya falsey sehingga direktif akan tetap berfungsi jika atribut itu tidak ditentukan.
cstricklan
Kontroler-as jauh lebih sederhana dengan lodash. _.set(scope, attributes.focusOnSaveInput, function() { element.focus(); }).
Atomosk
9

Anda dapat mencoba

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

untuk mis.

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

itu bekerja untuk saya.

Anoop
sumber
4
Catatan: Ini hanya akan berfungsi jika menggunakan jQuery penuh daripada mengandalkan jqLite yang disematkan di Angular. Lihat docs.angularjs.org/api/ng/function/angular.element
John Rix
4
Ini adalah cara jQuery untuk melakukan ini, bukan cara bersudut. Pertanyaan tersebut secara khusus menanyakan bagaimana melakukannya dengan cara bersudut.
forgivenson
4

Pilihan lain adalah menggunakan arsitektur pub-sub bawaan Angular untuk memberi tahu arahan Anda agar fokus. Mirip dengan pendekatan lain, tetapi kemudian tidak terkait langsung dengan properti, dan sebagai gantinya mendengarkan cakupannya untuk kunci tertentu.

Pengarahan:

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

Pengontrol:

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

Saya lebih suka menggunakan ekspresi. Ini memungkinkan saya melakukan hal-hal seperti fokus pada sebuah tombol ketika sebuah bidang valid, mencapai panjang tertentu, dan tentu saja setelah dimuat.

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

Pada bentuk yang kompleks, ini juga mengurangi kebutuhan untuk membuat variabel cakupan tambahan untuk tujuan pemfokusan.

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

winry
sumber