角度を付けて要素のフォーカスを設定する


113

角度でフォーカス要素を設定する方法の例を探した後、それらのほとんどはいくつかの変数を使用して監視してからフォーカスを設定し、ほとんどはフォーカスを設定したいフィールドごとに1つの異なる変数を使用しています。多くのフィールドを持つフォームでは、多くの異なる変数を意味します。

jqueryの方法を念頭に置いて、角度のある方法でそれを実行したいので、要素のIDを使用して任意の関数にフォーカスを設定するソリューションを作成しました。その方法は正しいです、問題がある、何でも、私がこれを角度でより良い方法で行うのを助けることができるものは何でも。

基本的に、ユーザーがディレクティブで定義したスコープ値、またはデフォルトのfocusElementを監視するディレクティブを作成します。その値が要素のIDと同じである場合、その要素はフォーカスを設定します。

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

何らかの理由でフォーカスを取得する必要がある入力は、このようになります

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

ここでフォーカスを設定する要素:

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

フォーカスを設定する関数の例:

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

それは角度の良い解決策ですか?私の貧しい経験でまだ見られない問題がありますか?

回答:


173

あなたのソリューションの問題は、新しいスコープを作成する他のディレクティブに結び付けられるとうまく機能しないことng-repeatです。より良い解決策は、コントローラー内で要素を強制的にフォーカスしたり、htmlで宣言的に要素をフォーカスしたりできるサービス関数を作成することです。

デモ

ジャバスクリプト

サービス

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

指令

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

コントローラ

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

私はこのソリューションが本当に好きです。しかし、$ timeoutをもう少し使用する理由を説明できますか?「Angular Thing」や「DOM Thing」のせいで使ったのですか?
user1821052 2014年

これは、angularが行うダイジェストサイクルの後に実行されることを確認しますが、これにより、タイムアウト後に実行される非同期アクションの後に影響を受けるダイジェストサイクルが除外されます。
ryeballar 2014年

3
ありがとう!これが角度付きドキュメントのどこで参照されているのか不思議に思う人のために、ここにリンク があります(永遠に探して見つけました)
user1821052

@ryeballar、ありがとう!素敵なシンプルなソリューション。ただ質問です。イベントが発生するのを待つ代わりに、属性を介して作成されたファクトリを使用できますか?
Pratik Gaikwad 2015

4
入力に焦点を合わせるために角度で必要な作業の量は非常識です。
Bruno Santos

19

このソリューションについては、ディレクティブを作成してDOM要素にアタッチするだけで、特定の条件が満たされたときにフォーカスを取得する必要があります。このアプローチに従うことにより、DOM要素IDへのコントローラーの結合を回避します。

サンプルコードディレクティブ:

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

可能な使用法

.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
myCondition$ scope変数がすでにtrueに設定されていて、ユーザーが別の要素にフォーカスすることを選択した場合、何が起きるかはまだわかりmyConditionますが、trueになっているときにフォーカスを再focusOnConditionトリガーできますか?変更しようとする値は同じです。
ryeballar 2014

サンプルを更新します。この例では、2つのラジオボタンがあり、値に応じてフラグをtrueまたはfalseに切り替えます。myConditionフラグをtrueまたはfalseに変更するだけです
Braulio

一般的なソリューションのようです。IDに依存するよりも優れています。私はそれが好きです。
mortb 2016年

他の誰かがこれを試してもうまくいかない場合は、element.focus()を変更する必要がありました。element [0] .focus();
エイドリアン・カー

1
このソリューションは、上記のIDベースのハックよりもはるかに「角度のある方法」です。
setec 2016

11

DOMルックアップ、ウォッチ、グローバルエミッターはできる限り避けたいので、より直接的なアプローチを使用します。ディレクティブを使用して、ディレクティブ要素に焦点を当てた単純な関数を割り当てます。次に、コントローラーのスコープ内の必要な場所でその関数を呼び出します。

これをスコープにアタッチするための簡単な方法を次に示します。controller-as構文の処理については、完全なスニペットをご覧ください。

指令:

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

そしてhtmlで:

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

またはコントローラーで:

$scope.focusOnSaveInput();

編集済みこのアプローチの理由についてより多くの説明を提供し、コントローラーを使用するためにコードスニペットを拡張するためにされました。


それは非常に素晴らしいことであり、私にとってはうまく機能しています。しかし、今はを使用して入力のセットがありng-repeat、最初のフォーカス関数のみを設定したいと思います。たとえば、条件に<input>基づいてフォーカス関数を条件付きで設定する方法はあり$indexますか?
Garret Wilson、

便利です。私の角度1は少し錆びていますが、のようassign-focus-function-if="{{$index===0}}"に入力に別の属性を追加できるはずです。そうでない場合は、関数の割り当て前にディレクティブの最初の行が早く終了するので、次の点をif (attr.assignFocusFunctionIf===false) return; 確認してください。これは明示的でfalseあり、単なる偽ではないため、その属性が定義されていなくてもディレクティブは機能します。
cstricklan 2018年

Controller-asは、lodashを使用することでかなり単純になります。_.set(scope, attributes.focusOnSaveInput, function() { element.focus(); })
アトモスク2018

9

あなたが試すことができます

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

たとえば。

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

それは私のために働いています。


4
注:これは、Angularに埋め込まれたjqLit​​eに依存するのではなく、完全なjQueryを使用する場合にのみ機能します。docs.angularjs.org/api/ng/function/angular.elementを
John Rix

4
これは、これを行うjQueryの方法であり、角度のある方法ではありません。質問は具体的にどのようにそれを角度のある方法で行うかを尋ねます。
forgivenson

4

別のオプションは、フォーカスするようにディレクティブに通知するために、Angularの組み込みpub-subアーキテクチャを使用することです。他のアプローチと同様ですが、プロパティに直接関連付けられておらず、特定のキーのスコープをリッスンしています。

指令:

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

コントローラ:

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

3

私は表現を使うことを好みました。これにより、フィールドが有効で、特定の長さに達したとき、そしてもちろんロード後に、ボタンにフォーカスするなどのことができます。

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

複雑なフォームでは、これにより、フォーカスの目的で追加のスコープ変数を作成する必要性も減少します。

https://stackoverflow.com/a/29963695/937997を参照してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.