Используя AngularJS, как я могу установить все поля формы на $грязные сразу?

Я создал форму HTML, используя AngularJS, и добавил атрибуты required к некоторым полям.

Для этих полей у меня есть сообщение об ошибке, которое отображает, если поле не $pristine, а также $invalid:

<input type="text" ng-model="SomeModel.SomeProperty" name="myField" class="input-block-level" required>
<p ng-show="frmMyForm.myField.$invalid && !frmMyForm.myField.$pristine" class="error-text">This field is required!</p>

Это прекрасно работает. Однако, если пользователь просто пропускает требуемое поле (никогда не помещает в него курсор), тогда поле всегда нетронуто, поэтому сообщение об ошибке не отображается даже после нажатия кнопки отправки. Таким образом, пользователь сталкивается с формой, которую они не могут отправить, но без текста ошибки, чтобы рассказать им, почему.

Моя мысль заключается в том, что установка всех полей формы в $dirty в действии отправки приведет к появлению сообщений об ошибках для любого требуемого поля, которое пользователь просто пропустил. Это возможно? Если да, то как?

Спасибо заранее.

Ответы

Ответ 1

Мы делаем что-то похожее на ваш ответ, у нас есть директива formSubmitted, которая привязывается к событию отправки, при запуске мы устанавливаем переменную $submit на контроллере формы. Таким образом, вы можете использовать его аналогично тому, как вы используете ShowValidationMessages, но его можно использовать повторно. Очень простая директива:

app.directive('formSubmitted', [function () {
    return {
        restrict: 'A',
        require: 'form',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$submitted = false;
            element.on('submit', function () {
                scope.$apply(function () {
                    ctrl.$submitted = true;
                });
            });
        }
    };
}]);

Вы применяете это в теге формы как атрибут.

Мы сделали еще пару шагов, наше требование состояло в том, чтобы показать ошибки проверки только в том случае, если выполняется следующее: этот элемент недействителен И либо форма была отправлена, либо элемент ввода размыт. Таким образом, мы закончили с другой директивой, которая требует ngModel, который устанавливает размытое состояние элемента на контроллере ngModel.

И наконец, чтобы избавиться от целого много повторяющегося кода котельной в html, чтобы проверить все эти вещи, например. ваш ng-show="frmMyForm.myField.$invalid && (!frmMyForm.myField.$pristine || MyObject.ShowValidationMessages)" мы инкапсулировали это в директиву. Эта директива шаблона обертывает наши входные элементы с помощью бот-плиты Bootstrap, а также обрабатывает все элементы проверки. Итак, теперь все мои входы формы следуют этому шаблону:

<div data-bc-form-group data-label="Username:">
    <input type="text" id="username" name="username" ng-model="vm.username" data-bc-focus required />
</div>

и директива bcFormGroup преобразуют ее в следующий загрузочный загрузочный html:

<div class="form-group" ng-class="{'has-error': showFormGroupError()}" data-bc-form-group="" data-label="Username:">
    <label for="username" class="col-md-3 control-label ng-binding">Username:</label>
    <div class="col-md-9">
        <input type="text" id="username" name="username" ng-model="vm.username" data-bc-focus="" required="" class="ng-pristine form-control ng-valid ng-valid-required">
        <span class="help-block ng-hide" ng-show="showRequiredError()">Required</span>
    </div>
</div>

Это держит вещи DRY и обеспечивает большую гибкость в том, какие типы входов поддерживаются.

Update:

Вот основной список директивы bcFormGroup. Шаблон по умолчанию использует горизонтальную форму бутстрапа, но может быть адаптирован по своему вкусу.

app.directive('bcFormGroup', ['$compile', '$interpolate', function ($compile, $interpolate) {
  return {
    restrict: 'A',
    template:
        '<div class="form-group" ng-class="{\'has-error\': showFormGroupError()}">' +
            '<label for="{{inputId}}" class="col-md-3 control-label">{{label}}</label>' +
            '<div class="col-md-9">' +
                '<bc-placeholder></bc-placeholder>' +
            '</div>' +
        '</div>',
    replace: true,
    transclude: true,
    require: '^form',
    scope: {
        label: '@',
        inputTag: '@'
    },

    link: function (scope, element, attrs, formController, transcludeFn) {

        transcludeFn(function (clone) {
            var placeholder = element.find('bc-placeholder');
            placeholder.replaceWith(clone);
        });

        var inputTagType = scope.inputTag || 'input';
        var inputElement = element.find(inputTagType);
        var fqFieldName = formController.$name + '.' + inputElement.attr('name');
        var formScope = inputElement.scope();

        if (inputElement.attr('type') !== 'checkbox' && inputElement.attr('type') !== 'file') {
            inputElement.addClass('form-control');
        }

        scope.inputId = $interpolate(inputElement.attr('id'))(formScope);
        scope.hasError = false;
        scope.submitted = false;

        formScope.$watch(fqFieldName + '.$invalid', function (hasError) {
            scope.hasError = hasError;
        });

        formScope.$watch(formController.$name + '.$submitted', function (submitted) {
            scope.submitted = submitted;
        });

        if (inputElement.attr('data-bc-focus') != null || inputElement.attr('bc-focus') != null) {
            scope.hasBlurred = false;
            formScope.$watch(fqFieldName + '.$hasBlurred', function (hasBlurred) {
                scope.hasBlurred = hasBlurred;
            });
        }

        if (inputElement.attr('required')) {
            scope.hasRequiredError = false;
            formScope.$watch(fqFieldName + '.$error.required', function (required) {
                scope.hasRequiredError = required;
            });
            inputElement.after($compile('<span class="help-block" ng-show="showRequiredError()">Required</span>')(scope));
        }

        if (inputElement.attr('type') === 'email') {
            scope.hasEmailError = false;
            formScope.$watch(fqFieldName + '.$error.email', function (emailError) {
                scope.hasEmailError = emailError;
            });
            inputElement.after($compile('<span class="help-block" ng-show="showEmailError()">Invalid email address</span>')(scope));
        }

        scope.showFormGroupError = function () {
            return scope.hasError && (scope.submitted || (scope.hasBlurred === true));
        };

        scope.showRequiredError = function () {
            return scope.hasRequiredError && (scope.submitted || (scope.hasBlurred === true));
        };

        scope.showEmailError = function () {
            return scope.hasEmailError && (scope.submitted || (scope.hasBlurred === true));
        };

    }
  };
}]);

Update:

Следующая директива устанавливает $focus и $hasBlurred:

app.directive('bcFocus', [function () {
    var focusClass = 'bc-focused';
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$focused = false;
            ctrl.$hasBlurred = false;
            element.on('focus', function () {
                element.addClass(focusClass);
                var phase = scope.$root.$$phase;
                if (phase == '$apply' || phase == '$digest') {
                    ctrl.$focused = true;
                } else {
                    scope.$apply(function () {
                        ctrl.$focused = true;
                    });
                }
            }).on('blur', function () {
                element.removeClass(focusClass);
                var phase = scope.$root.$$phase;
                if (phase == '$apply' || phase == '$digest') {
                    ctrl.$focused = false;
                    ctrl.$hasBlurred = true;
                } else {
                    scope.$apply(function () {
                        ctrl.$focused = false;
                        ctrl.$hasBlurred = true;
                    });
                }
            });
        }
    };
}]);

Ответ 2

Если форма недействительна, FormController содержит свойство $error.

$error Является хешем объекта, содержащим ссылки на элементы управления или формы с отказоустойчивыми валидаторами.

Итак, вы можете просто пропустить объект ошибки и использовать NgModelController $setDirty() для каждого элемента управления:

// "If the name attribute is specified, the form controller is published onto the current scope under this name."
var form = scope.myForm;

if (form.$invalid) {
    angular.forEach(form.$error, function(controls, errorName) {
        angular.forEach(controls, function(control) {
            control.$setDirty();
        });
    });
}

Ответ 3

Я придумал ответ вскоре после публикации этого. Я не уверен, что это правильный путь, но он работает.

В вашем контроллере просто добавьте свойство "ShowValidationMessages" или что-то подобное и установите его на false:

$scope.MyObject = {
   ShowValidationMessages: false
};

Теперь укажите это значение в логике проверки на уровне поля:

<p ng-show="frmMyForm.myField.$invalid && (!frmMyForm.myField.$pristine || MyObject.ShowValidationMessages)" class="error-text">This field is required!</p>

Наконец, переключите свойство ShowValidationMessages на true в функции отправки формы:

    $scope.MyObject = {
       ShowValidationMessages: false,
       SubmitForm: function(){
            $scope.MyObject.ShowValidationMessages = true;
            if($scope.frmMyForm.$valid){
                //do stuff
            }    
       }
    };

Ответ 4

Я сделал что-то простое.

 $scope.FormName.InputName.$pristine = false;
if($scope.FormName.$invalid)
return;