61

AngularJs でのフォーカスの設定に関するいくつかの記事と StackOverflow の質問を読みました。

残念ながら、私が読んだすべての例は、focusMe ディレクティブなど、要素に追加してフォーカスを得ることができる属性があることを前提としています。

ただし、フォーカスを設定する入力を事前に知らない場合はどうなりますか? 特に、$invalid が設定されているフォームの最初の入力要素、つまり検証に失敗した要素にフォーカスを設定するにはどうすればよいですか。検証に失敗する入力がいくつかある可能性があるため、これに基づいて .focus() を呼び出そうとするだけのディレクティブは使用できません。(私はアクセシビリティ/WCAG の理由でこれを行っています。検証に失敗した最初のフィールドを見つけるためにキープレスを最小限に抑えるために、送信時にクリックすることをお勧めします)。

$error オブジェクトは、検証に失敗したすべてのコントロールを提供しますが、フォームに表示される順序ではなく、失敗の種類によってグループ化されます。

私はこれを行うためのいくつかの巧妙な方法を思い付くことができると確信しています。フォーカスを設定する必要があるときに何らかのブロードキャストを受信するフォーム上のディレクティブ - そのディレクティブは、最初の $invalid 要素を検索できます。ただし、これは非常に複雑に思えます。これがより「角度のある」方法であるかどうかを知りたいです。

4

13 に答える 13

87

わかりましたので、答えは思ったより簡単でした。

必要なのは、送信イベントを検索するイベント ハンドラーを使用して、フォーム自体に配置するディレクティブだけでした。次に、DOM を走査して、.ng-invalid クラスを持つ最初の要素を探します。

jQLite を使用した例:

myApp.directive('accessibleForm', function () {
    return {
        restrict: 'A',
        link: function (scope, elem) {

            // set up event handler on the form element
            elem.on('submit', function () {

                // find the first invalid element
                var firstInvalid = elem[0].querySelector('.ng-invalid');

                // if we find one, set focus
                if (firstInvalid) {
                    firstInvalid.focus();
                }
            });
        }
    };
});

この例では Attribute ディレクティブを使用しています。この例を拡張して、これを要素ディレクティブ (制限: 'E') にし、これを に変換するテンプレートを含めることができます。ただし、これは個人的な好みです。

于 2013-12-05T01:06:15.403 に答える
17

angular.element を使用することもできます

angular.element('input.ng-invalid').first().focus();

意見

<form name="myForm" novalidate="novalidate" data-ng-submit="myAction(myForm.$valid)" autocomplete="off"></form>

コントローラ

$scope.myAction= function(isValid) {
    if (isValid) {
        //You can place your ajax call/http request here
    } else {
        angular.element('input.ng-invalid').first().focus();
    }
};

検証に ngMessages を使用

jquery を使用しない方法

angular.element($document[0].querySelector('input.ng-invalid')).focus();

このメソッドを使用する場合$documentは、角度コントローラーでパラメーターとして渡す必要があります

angular.module('myModule')
.controller('myController', ['$document', '$scope', function($document, $scope){
    // Code Here
}]);
于 2016-03-16T15:20:52.440 に答える
15

他の回答としてディレクティブを作成することも、それをフックしng-submitてコントローラーにロジックを実装することもできます。

意見:

<form name='yourForm' novalidate ng-submit="save(yourForm)">
</form>

コントローラ:

$scope.save = function(yourForm) {
  if (!yourForm.$valid) {
    angular.element("[name='" + yourForm.$name + "']").find('.ng-invalid:visible:first').focus();
    return false;
  }
};
于 2015-10-30T02:05:50.373 に答える
7

純粋な jQuery を使用して、最初の無効な入力を選択できます。

$('input.ng-invalid').first().focus();

于 2015-04-02T05:28:46.457 に答える
4

    .directive('accessibleForm', function () {
        return {
            restrict: 'A',
            link: function (scope, elem) {
                // set up event handler on the form element
                elem.on('submit', function () {
                    // find the first invalid element
                    var firstInvalid = elem[0].querySelector('.ng-invalid');
                    if (firstInvalid && firstInvalid.tagName.toLowerCase() === 'ng-form') {
                        firstInvalid = firstInvalid.querySelector('.ng-invalid');
                    }
                    // if we find one, set focus
                    if (firstInvalid) {
                        firstInvalid.focus();
                    }
                });
            }
        };
    })

于 2015-10-23T10:43:16.003 に答える
2

私はしばらくの間このアイデアを試してきましたが、独自の解決策を思いつきました。これは、私のように DOM のクロールに反対する人々を助けるかもしれません。

私が知る限り、フォーム要素は一貫した順序 (つまり、上から下) で登録され、その名前と検証状態は、フォーム名が何であれ (例: $scope.myForm) スコープで利用できます。

これにより、DOM をクロールせずに、angular js の内部構造をクロールする代わりに、最初の無効なフォーム入力を見つける方法があると思いました。以下は私の解決策ですが、フォーム要素にフォーカスする他の方法があることを前提としています。ブロードキャストが要素の名前と一致する場合、カスタム ディレクティブにブロードキャストしています。最初のロードでどの要素がフォーカスされるかを制御します)。

最初の無効を見つける関数 (理想的には、サービスを介してコントローラーに共有されます)

function findFirstInvalid(form){
    for(var key in form){
        if(key.indexOf("$") !== 0){
            if(form[key].$invalid){
                return key;
            }
        }
    }
}

そして、カスタム フォーカス ディレクティブ

directives.directive('focus', function($timeout){
    return {
        require: 'ngModel',
        restrict: 'A',
        link: function(scope, elem, attrs, ctrl){
            scope.$on('inputFocus', function(e, name){
                if(attrs.name === name){
                    elem.focus();
                }
            });
        }
    }
});
于 2014-02-20T04:52:50.183 に答える
0

上記のchaojidanに触発されて、ネストされた angular 1.5.9 ng-forms を使用している人にこのバリエーションを提案しました。

class FormFocusOnErr implements ng.IDirective
{
    static directiveId: string = 'formFocusOnErr';

    restrict: string = "A";

    link = (scope: ng.IScope, elem, attrs) =>
    {
        // set up event handler on the form element
        elem.on('submit', function () {

            // find the first invalid element
            var firstInvalid = angular.element(
                elem[0].querySelector('.ng-invalid'))[0];

            // if we find one, set focus
            if (firstInvalid) {
                firstInvalid.focus();
                // ng-invalid appears on ng-forms as well as 
                // the inputs that are responsible for the errors.
                // In such cases, the focus will probably fail 
                // because we usually put the ng-focus attribute on divs 
                // and divs don't support the focus method
                if (firstInvalid.tagName.toLowerCase() === 'ng-form' 
                    || firstInvalid.hasAttribute('ng-form') 
                    || firstInvalid.hasAttribute('data-ng-form')) {
                    // Let's try to put a finer point on it by selecting 
                    // the first visible input, select or textarea 
                    // that has the ng-invalid CSS class
                    var firstVisibleInvalidFormInput = angular.element(firstInvalid.querySelector("input.ng-invalid,select.ng-invalid,textarea.ng-invalid")).filter(":visible")[0];
                    if (firstVisibleInvalidFormInput) {
                        firstVisibleInvalidFormInput.focus();
                    }
                }
            }
        });            
    }
}

// Register in angular app
app.directive(FormFocusOnErr.directiveId, () => new FormFocusOnErr());
于 2016-10-24T22:04:40.000 に答える
-1

非ディレクティブ ベースのメソッドは次のようになります。実際にはフッターのindex.htmlにある各ページの下部に「次へ」ボタンがあるため、これを使用しました。このコードは main.js で使用します。

if (!$scope.yourformname.$valid) {
      // find the invalid elements
      var visibleInvalids = angular.element.find('.ng-invalid:visible');


      if (angular.isDefined(visibleInvalids)){
        // if we find one, set focus
        visibleInvalids[0].focus();
      }

      return;
    }
于 2015-06-26T15:25:59.023 に答える