Установите фокус на первый недопустимый элемент формы в AngularJS

По сути, то, что я пытаюсь выполнить, - это установить фокус на первый недопустимый элемент после попытки отправки формы. На данный момент у меня есть элемент, помеченный как недопустимый, и я могу получить $name элемента, чтобы я знал, какой это элемент.

Это "работает", но выдается ошибка "$apply уже выполняется"...
Так что я, должно быть, делаю что-то не так :)

Вот мой код:

$scope.submit = function () {

    if ($scope.formName.$valid) {
        // Good job.
    }
    else 
    {
        var field = null,
            firstError = null;
        for (field in $scope.formName) {
            if (field[0] != '$')
            {
                if (firstError === null && !$scope.formName[field].$valid) {
                    firstError = $scope.formName[field].$name;
                }

                if ($scope.formName[field].$pristine) {
                    $scope.formName[field].$dirty = true;
                }
            }
        }

        formName[firstError].focus();
    }
}

Мое зацикливание полей основано на этом решении, и я прочитал над этим вопросом несколько раз. Кажется предпочтительным решением является создание директивы, но добавление директивы к каждому отдельному элементу формы кажется излишним.

Есть ли лучший способ приблизиться к этому с помощью директивы?


person Kara    schedule 07.11.2013    source источник


Ответы (3)


Код директивы:

app.directive('ngFocus', function ($timeout, $log) {
return {
    restrict: 'A',
    link: function (scope, elem, attr) {

        scope.$on('focusOn', function (e, name) {
            // The timeout lets the digest / DOM cycle run before attempting to set focus
            $timeout(function () {
                if (name === attr.ngFocusId) {
                    if (attr.ngFocusMethod === "click")
                        angular.element(elem[0]).click();
                    else
                        angular.element(elem[0]).focus();
                }
            });
        })
    }
}
});

Фабрика для использования в контроллере:

app.factory('focus', function ($rootScope, $timeout) {
    return function (name) {
        $timeout(function () {
            $rootScope.$broadcast('focusOn', name);
        }, 0, false);
    };
});

Пример контроллера:

angular.module('test', []).controller('myCtrl', ['focus', function(focus) {
  focus('myElement');
}
person jklemmack    schedule 03.01.2014
comment
$timeout был очень полезным советом. Спасибо! - person Jon Stevens; 20.02.2014

Создание директивы, безусловно, путь. В противном случае нет чистого способа выбрать элемент в angularjs. Это просто не так спроектировано. Я бы порекомендовал вам проверить это вопрос по этому поводу.

Вам не нужно было бы создавать одну директиву для каждого элемента формы. По для каждой формы должно хватить. Внутри директивы вы можете использовать element.find('input');. Для самого фокуса, я полагаю, вам нужно включить jQuery и использовать его функцию фокуса.

Однако вы можете - и я бы этого не рекомендовал - использовать jQuery непосредственно внутри вашего контроллера. Обычно проверка угловой формы добавляет такие классы, как ng-invalid-required и тому подобное, которые вы можете использовать в качестве селектора. например:

$('input.ng-valid').focus();
person hugo der hungrige    schedule 08.11.2013
comment
Разве этот подход не будет просто директивами использования в качестве места, где можно применить кучу подходов jQuery, упомянутых в вопросе, который вы связали? В этом случае я не уверен, что element.findдостаточно, так как мне нужен первый элемент invalid, а не только первый элемент формы на странице... или я что-то упустил? - person Kara; 08.11.2013
comment
Вы можете использовать ng-invalid или ng-invalid-required. Это может показаться странным, так как вы можете сэкономить много кода, просто используя jQuery. Вот почему я рекомендую прочитать ответы на вопрос выше. Одним из преимуществ создания директивы является то, что вы не только можете легко повторно использовать ее в других формах, но, что более важно, она лучше соответствует мышлению, что позволяет вам в целом писать лучший angularjs-код. Я думаю, хорошо, что вам обычно нужно просто взглянуть на шаблон, который позволяет легко увидеть, что происходит на странице. - person hugo der hungrige; 10.11.2013

Основываясь на отзывах Хьюго, мне удалось составить директиву:

.directive( 'mySubmitDirty', function () {
    return {
        scope: true,
        link: function (scope, element, attrs) {

            var form = scope[attrs.name];

            element.bind('submit', function(event) {
                var field = null;
                for (field in form) {
                    if (form[field].hasOwnProperty('$pristine') && form[field].$pristine) {
                        form[field].$dirty = true;
                    }
                }

                var invalid_elements = element.find('.ng-invalid');
                if (invalid_elements.length > 0)
                {
                   invalid_elements[0].focus();
                }

                event.stopPropagation();
                event.preventDefault();
            });
        }
    };
})

Для этого подхода требуется jquery, поскольку element.find() использует класс для поиска первого недопустимого элемента в dom.

person Kara    schedule 08.11.2013
comment
element.find('.ng-invalid') это здорово, я не думал об этом, но на самом деле он работает очень чисто - person Kat Lim Ruiz; 13.06.2014