Öğe odağını açısal olarak ayarlayın


113

Odak öğelerinin açısal olarak nasıl ayarlanacağına dair örnekler aradıktan sonra, çoğunun izlemek için bazı değişkenler kullandıklarını ve çoğunun odak ayarlamak istedikleri her alan için bir farklı değişken kullandığını gördüm. Çok sayıda alana sahip bir formda, bu birçok farklı değişkeni ifade eder.

Jquery yolunu göz önünde bulundurarak, ancak bunu açısal bir şekilde yapmak isteyerek, öğenin kimliğini kullanarak herhangi bir işleve odaklandığımız bir çözüm yaptım, bu nedenle, açısal olarak çok yeniyim, eğer bu yol doğrudur, sorun yaşarsınız, her neyse, bunu açısal olarak daha iyi yapmama yardımcı olabilecek herhangi bir şey.

Temel olarak, kullanıcı tarafından yönergeyle veya varsayılanın focusElement ile tanımlanan bir kapsam değerini izleyen bir yönerge oluşturuyorum ve bu değer öğenin kimliği ile aynı olduğunda, bu öğe odağı kendisi ayarlıyor.

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

Herhangi bir nedenle odaklanması gereken bir girdi bu şekilde işe yarar

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

Burada odaklanmak için herhangi bir öğe:

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

Ve odağı belirleyen örnek işlev:

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

Bu açısal olarak iyi bir çözüm mü? Zayıf tecrübemle henüz görmediğim sorunları var mı?

Yanıtlar:


173

Çözümünüzle ilgili sorun, yeni bir kapsam oluşturan diğer direktiflere bağlandığında iyi çalışmamasıdır, örn ng-repeat. Daha iyi bir çözüm, öğeleri denetleyicilerinize zorunlu olarak odaklamanıza veya öğeleri html'de bildirimli olarak odaklamanıza olanak tanıyan bir hizmet işlevi oluşturmaktır.

DEMO

JAVASCRIPT

Hizmet

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

Direktif

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

kontrolör

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

Bu çözümü gerçekten beğendim. Bununla birlikte, $ timeout kullanmanın nedenini biraz daha açıklayabilir misiniz? "Angular Thing" veya "DOM Thing" nedeniyle kullanmanızın nedeni mi?
user1821052

Açısal olan herhangi bir özet döngüsünden sonra çalıştığından emin olur, ancak bu, zaman aşımından sonra yürütülen eşzamansız bir eylemden sonra etkilenen özet döngülerini hariç tutar.
ryeballar

3
Teşekkürler! Açısal belgelerde buna nereden atıfta bulunulduğunu merak edenler için, işte bağlantı (bulmam sonsuza kadar sürdü)
user1821052

@ryeballar, Teşekkürler !. Güzel ve basit bir çözüm. Yine de bir soru. Bir olayın olmasını beklemek yerine öznitelik aracılığıyla oluşturulan fabrikayı kullanabilir miyim?
Pratik Gaikwad

4
Bir girdiye odaklanmak için açısal olarak gerekli olan iş miktarı deliliktir.
Bruno Santos

19

Bu çözümle ilgili olarak, bir yönerge oluşturabilir ve bunu, belirli bir koşul karşılandığında odaklanması gereken DOM öğesine ekleyebiliriz. Bu yaklaşımı izleyerek, denetleyiciyi DOM öğesi kimliklerine bağlamaktan kaçınıyoruz.

Örnek kod yönergesi:

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

Olası bir kullanım

.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
Ne zaman olacağını myConditionhala odak tetikleyici olabilir, $ kapsamı değişken zaten true seti ve başka elemana odaklanmaya sonra kullanıcı seçer olmuştur myConditionzaten doğrudur, sizin kod özelliği için değişiklikleri izler focusOnConditionama tetik zaman olmaz Değiştirmeye çalıştığınız değer hala aynı.
ryeballar

Örneği güncelleyeceğim, bizim durumumuzda iki radyo düğmemiz var ve değere bağlı olarak bayrağı doğru veya yanlış olarak değiştiririz, myCondition bayrağını doğru veya yanlış olarak değiştirebilirsiniz
Braulio

Genel bir çözüm gibi görünüyor. Kimliklere bağlı olmaktan daha iyi. Bunu sevdim.
mortb

Başka birinin bunu denemesi ve işe yaramaması durumunda, element.focus () 'u değiştirmek zorunda kaldım; [0] .focus () öğesine;
Adrian Carr

1
Bu çözüm, yukarıdaki kimlik tabanlı hacklemeden çok daha 'açısal bir yoldur'.
setec

11

DOM aramalarından, saatlerinden ve global yayıcılardan mümkün olduğunca kaçınmayı seviyorum, bu nedenle daha doğrudan bir yaklaşım kullanıyorum. Yönerge öğesine odaklanan basit bir işlev atamak için bir yönerge kullanın. Ardından, kontrolör kapsamında ihtiyaç duyulan her yerde bu işlevi çağırın.

İşte kapsama eklemek için basitleştirilmiş bir yaklaşım. Denetleyici sözdizimi olarak işlemek için tam snippet'e bakın.

Direktif:

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

ve html'de:

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

veya kontrolörde:

$scope.focusOnSaveInput();

Düzenlenen bu yaklaşımın nedeni hakkında daha fazla açıklama getirmek ve denetleyici-olarak kullanım için kod parçacığını uzatmak için.


Bu çok güzel ve benim için iyi çalışıyor. Ama şimdi kullanan bir dizi girdim var ng-repeatve sadece birincisi için odak işlevini ayarlamak istiyorum. Örneğin <input>temel alınan bir odak işlevini koşullu olarak nasıl ayarlayabileceğime dair bir fikriniz var $indexmı?
Garret Wilson

Yararlı olmasına sevindim. Benim 1 biraz paslanmış, ancak girişine başka bir özellik eklemek gerekir, gibi köşeli assign-focus-function-if="{{$index===0}}"ve ardından yönerge çıkışında ilk satırı olarak erken bu doğru değilse bir işlevi atamadan önce: if (attr.assignFocusFunctionIf===false) return; Ben eğer kontrol ediyorum Not açık bir şekilde falseve sadece falsey değil, dolayısıyla bu öznitelik tanımlanmamışsa yönerge yine de çalışacaktır.
cstricklan

Kontrolör - lodash ile çok daha basittir. _.set(scope, attributes.focusOnSaveInput, function() { element.focus(); }).
Atomosk

9

Deneyebilirsin

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

örneğin.

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

benim için çalışıyor.


4
Not: Bu, Angular'da gömülü jqLite'a güvenmek yerine yalnızca tam jQuery kullanıldığında işe yarar. Bkz. Docs.angularjs.org/api/ng/function/angular.element
John Rix

4
Bunu yapmanın jQuery yolu budur, açısal bir yol değil. Soru özellikle bunun açısal bir şekilde nasıl yapılacağını soruyor.
forgivenson

4

Diğer bir seçenek, direktifinize odaklanmanız için bildirimde bulunmak üzere Angular'ın yerleşik pub-sub mimarisini kullanmak olacaktır. Diğer yaklaşımlara benzer, ancak o zaman bir mülke doğrudan bağlı değildir ve bunun yerine belirli bir anahtar için onun kapsamını dinler.

Direktif:

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

Denetleyici:

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

3

Bir ifade kullanmayı tercih ettim. Bu, bir alan geçerli olduğunda, belirli bir uzunluğa ulaştığında ve elbette yükten sonra bir düğmeye odaklanmak gibi şeyler yapmamı sağlıyor.

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

Karmaşık bir formda bu, odaklanma amaçları için ek kapsam değişkenleri oluşturma ihtiyacını da azaltır.

Bkz. Https://stackoverflow.com/a/29963695/937997

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.