wake-up-neo.com

Setzen Sie den Fokus auf die erste ungültige Eingabe im AngularJs-Formular

Ich habe mehrere Artikel und StackOverflow-Fragen zur Einstellung des Fokus in AngularJs gelesen.

Leider gehen alle Beispiele, die ich gelesen habe, davon aus, dass dem Element ein Attribut hinzugefügt werden kann, um den Fokus zu erhalten, z. eine focusMe-Direktive .

Was aber, wenn ich nicht im Voraus weiß, auf welchen Eingang der Fokus eingestellt werden soll? Wie kann ich insbesondere den Fokus auf das erste Eingabeelement in einem Formular setzen, für das $ invalid gesetzt ist - d. H. Ein Element, das die Validierung nicht bestanden hat. Möglicherweise gibt es mehrere Eingaben, deren Validierung fehlschlägt. Ich kann also keine Direktive verwenden, die nur versucht, .focus () darauf aufzurufen. (Ich tue dies aus Gründen der Barrierefreiheit/des WCAG. Es ist empfehlenswert, beim Klicken auf "Senden" zu klicken, um das Drücken der Tasten zu minimieren, um das erste Feld zu finden, dessen Validierung fehlgeschlagen ist.).

Das $ error-Objekt gibt alle Steuerelemente an, deren Validierung fehlgeschlagen ist. Sie werden jedoch nach der Art des Fehlers gruppiert, jedoch nicht in der Reihenfolge des Auftretens im Formular.

Ich bin mir sicher, dass ich mir dazu einige klapprige Wege machen kann. Eine Direktive auf dem Formular, die etwas Broadcast empfängt, wenn der Fokus gesetzt werden muss - diese Direktive kann dann nach dem ersten ungültigen Element suchen. Dies scheint jedoch sehr komplex zu sein, und ich würde gerne wissen, ob dies eine eher „kantigere“ Vorgehensweise ist.

59
iandotkelly

Ok, also war die Antwort einfacher als ich dachte.

Alles, was ich brauchte, war eine Anweisung zum Ausfüllen des Formulars selbst. Ein Event-Handler suchte nach dem Submit-Event. Dadurch kann das DOM nach dem ersten Element durchsucht werden, das die Klasse .ng-invalid enthält.

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

In diesem Beispiel wird eine Attribut-Direktive verwendet. Sie können das Beispiel so erweitern, dass eine Element-Direktive (Einschränkung: 'E') verwendet wird, und eine Vorlage einfügen, die diese in eine konvertiert. Dies ist jedoch eine persönliche Präferenz.

87
iandotkelly

Sie können Anweisungen als andere Antworten erstellen oder alternativ mit ng-submit verknüpfen und Logik in der Steuerung implementieren.

Aussicht:

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

Regler:

$scope.save = function(yourForm) {
  if (!yourForm.$valid) {
    angular.element("[name='" + yourForm.$name + "']").find('.ng-invalid:visible:first').focus();
    return false;
  }
};
15
nnattawat

Sie können auch angle.element verwenden

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

Aussicht

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

Controller

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

ngMessages zur Validierung verwendet

Der Weg ohne Jquery

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

Wenn Sie diese Methode verwenden, müssen Sie $document als Parameter in Ihrem Winkelregler übergeben

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

Sie können pure jQuery verwenden, um die erste ungültige Eingabe auszuwählen:

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

7
Edmond Chui

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

4
chaojidan

Ich habe schon eine Weile mit dieser Idee gespielt, und ich habe meine eigene Lösung gefunden. Sie kann Menschen helfen, die das DOM nicht mögen, wie ich.

Soweit ich erkennen kann, registrieren sich Formularelemente in einer konsistenten Reihenfolge (d. H. Von oben nach unten), und ihre Namen und Validierungszustände sind im Gültigkeitsbereich durch den Namen des Formularnamens (z. B. $ scope.myForm) verfügbar.

Dies führte mich zu der Annahme, dass es eine Möglichkeit gab, die erste ungültige Formulareingabe zu finden, ohne das DOM zu crawlen und stattdessen die internen Strukturen von angle js zu crawlen. Hier ist meine Lösung, aber es wird davon ausgegangen, dass Sie eine andere Möglichkeit haben, Formularelemente zu fokussieren. Ich stelle eine benutzerdefinierte Anweisung aus. Wenn die Übertragung mit dem Namen des Elements übereinstimmt, wird es selbst fokussiert (was an sich schon nützlich ist, wenn Sie dazu kommen steuern, welches Element bei der ersten Last den Fokus hat).

Die Funktion zum Finden des ersten ungültigen Objekts (idealerweise für die Controller über einen Dienst freigegeben)

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

Und die Custom Focus Direktive

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

Ich habe einige kleine Änderungen an der großartigen Lösung vorgenommen, die von iandotkelly geschrieben wurde .. Diese Lösung fügt eine Animation hinzu, die beim Scrollen ausgelöst wird, und fokussiert das ausgewählte Element danach.

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

nur eine Zeile: 

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

Das liegt daran, dass focus() in jqLite und den Angular-Dokumenten für element nicht unterstützt wird.

0
Acacio Martins

Ein kleiner Tweak mit dem, was @Sajan gesagt hat, hat bei mir funktioniert.

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

Ich wurde von chaojidan oben inspiriert, um diese Variante für diejenigen vorzuschlagen, die verschachtelte eckige 1.5.9 ng-Formen verwenden:

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());
0
CAK2

Sie können in jedes Formularelement ein Attribut einfügen, das eine Funktion (idealerweise eine Direktive) ist, die eine Feld-ID erhält. Diese Feld-ID müsste irgendwie mit Ihrem $ error-Objekt korrelieren. Die Funktion kann überprüfen, ob sich die ID in Ihrem $ error-Objekt befindet und wenn dies der Fall ist, geben Sie die Attributeinstellung für einen Fehler zurück. 

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

Wenn Sie einen Fehler hatten, würde dies dies generieren.

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

Sie können dies verwenden, um Ihren Stil festzulegen, und Sie wissen jetzt, welche Felder Fehler enthalten. Leider wissen Sie nicht, welches das erste Feld ist. 

Eine Lösung wäre die Verwendung von jQuery und dem ersten Filter. Wenn Sie diese Route wählen, besuchen Sie http://docs.angularjs.org/api/angular.element

Eine andere Lösung wäre, in Ihre Formularfelder einen Feldreihenfolgeparameter für die Funktion einzufügen: {{errorCheck ('name', 1)}}. Sie können die Fehlerfeldnamen in ein Array verschieben und dann nach dem Parameter für die Feldreihenfolge sortieren. Dies könnte Ihnen mehr Flexibilität geben.

Hoffe das hilft.

0
Darryl