UFO ET IT

AngularJs 양식의 첫 번째 잘못된 입력에 초점 설정

ufoet 2020. 12. 3. 21:08
반응형

AngularJs 양식의 첫 번째 잘못된 입력에 초점 설정


AngularJs의 포커스 설정과 관련된 여러 기사와 StackOverflow 질문을 읽었습니다.

불행히도 내가 읽은 모든 예제는 포커스를 얻기 위해 요소에 추가 할 수있는 몇 가지 속성 (예 : focusMe 지시문)있다고 가정합니다 .

그러나 포커스를 설정할 입력을 미리 알지 못하면 어떻게합니까? 특히 $ invalid 세트가있는 양식의 첫 번째 입력 요소, 즉 유효성 검사에 실패한 요소에 포커스를 설정하는 방법은 무엇입니까? 유효성 검사에 실패하는 입력이 여러 개있을 수 있으므로이를 기반으로 .focus () 호출을 시도하는 지시문을 사용할 수 없습니다. (저는 접근성 / WCAG 이유로이 작업을 수행하고 있으며, 확인에 실패한 첫 번째 필드를 찾기 위해 키 누름을 최소화하기 위해 제출을 클릭하는 것이 좋습니다).

$ error 개체는 유효성 검사에 실패한 모든 컨트롤을 제공하지만 양식에 나타나는 순서가 아닌 실패 유형별로 그룹화됩니다.

이 작업을 수행하는 약간의 방법을 생각해 낼 수 있다고 확신합니다. 포커스를 설정해야 할 때 일부 브로드 캐스트를 수신하는 양식의 지시문-해당 지시문은 첫 번째 $ invalid 요소를 검색 할 수 있습니다. 그러나 이것은 매우 복잡해 보이며 이것이 더 나은 '각진'방법인지 알고 싶습니다.


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

유효성 검사를 위해 ngMessage를 사용했습니다.

no jquery 방식

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

이 방법을 사용할 때 $document각도 컨트롤러에서 매개 변수 로 전달해야합니다.

angular.module('myModule')
.controller('myController', ['$document', '$scope', function($document, $scope){
    // Code Here
}]);

네, 대답은 생각보다 간단했습니다.

필요한 것은 제출 이벤트를 찾는 이벤트 핸들러와 함께 양식 자체에 배치하는 지시문뿐이었습니다. 그러면 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 지시문을 사용하며,이를 요소 지시문 (restrict : 'E')이되도록 예제를 확장하고이를. 그러나 이것은 개인적인 취향입니다.


다른 답변으로 지시문을 만들거나 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;
  }
};

순수 jQuery를 사용하여 첫 번째 잘못된 입력을 선택할 수 있습니다.

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


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


나는 한동안이 아이디어를 가지고 놀았고, 나처럼 DOM 크롤링에 불리한 사람들에게 도움이 될 수있는 나만의 솔루션을 생각해 냈습니다.

양식 요소가 일관된 순서 (즉, 위에서 아래로)로 등록되고 양식 이름이 무엇이든 (예 : $ scope.myForm) 범위에서 해당 이름과 유효성 검사 상태를 사용할 수 있습니다.

이로 인해 DOM을 크롤링하지 않고 대신 각 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();
                }
            });
        }
    }
});

iandotkelly가 작성한 훌륭한 솔루션을 약간 수정했습니다. 이 솔루션은 스크롤시 트리거되는 애니메이션을 추가하고 그 후에 선택한 요소에 초점을 맞 춥니 다.

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, we scroll with animation and then we set focus
                if (firstInvalid) {
                     angular.element('html:not(:animated),body:not(:animated)')
                    .animate({ scrollTop: angular.element(firstInvalid).parent().offset().top },
                        350,
                        'easeOutCubic',
                        function () {
                            firstInvalid.focus();
                        });
                }
            });
        }
    };
});

한 줄만 :

if($scope.formName.$valid){
    //submit
}
else{
    $scope.formName.$error.required[0].$$element.focus();
}

필드 ID를받는 함수 (이상적으로는 지시문) 인 각 양식 요소에 속성을 추가 할 수 있습니다. 이 필드 ID는 $ error 객체와 어떻게 든 상관 관계가 있어야합니다. 이 함수는 ID가 $ error 객체에 있는지 확인하고, 그렇다면 오류에 대한 속성 설정을 반환합니다.

<input id="name" class="{{errorCheck('name')}}">

오류가 발생하면이를 생성합니다.

<input id="name" class="error">

이를 사용하여 스타일을 설정할 수 있으며 이제 오류가있는 필드를 알 수 있습니다. 불행히도 당신은 첫 번째 필드가 무엇인지 모릅니다.

한 가지 해결책은 jQuery와 .first 필터를 사용하는 것입니다. 이 경로로 가면 http://docs.angularjs.org/api/angular.element를 확인 하십시오.

Another solution would be to add into your form fields a field order parameter for the function: {{errorCheck('name', 1)}}. You could push the error field names to an array, then sort them by the field order parameter. This could give you more flexibility.

Hope this helps.


I was inspired by chaojidan above to suggest this variation for those who are using nested 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());

That's because focus() is not supported in jqLite and from the Angular docs on element.


A minor tweak with what @Sajan said worked for me,

angular.element("[name='" + this.formName.$name + "']").find('.ng-invalid:visible:first')[0].focus();

A non-directive based method could look like this. It is what i used, since i have a 'next' button at the bottom of each page that is actually in index.html in the footer. I use this code in 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;
    }

참고URL : https://stackoverflow.com/questions/20365121/set-focus-on-first-invalid-input-in-angularjs-form

반응형