Как мне сбросить форму, включая удаление всех ошибок валидации?

У меня есть угловая форма. Поля проверены с использованием ng-pattern приписывать. У меня также есть кнопка сброса. Я использую связыватель событий Ui.Utils для обработки reset событие вроде так:

<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
  <div>
    <label>
      Area Code
      <input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
    </label>

    <div ng-messages="searchForm.areaCode.$error">
      <div class="error" ng-message="pattern">The area code must be three digits</div>
    </div>
  </div>

  <div>
    <label>
      Phone Number
      <input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
    </label>

    <div ng-messages="searchForm.phoneNumber.$error">
      <div class="error" ng-message="pattern">The phone number must be seven digits</div>
    </div>
  </div>

  <br>
  <div>
    <button type="reset">Reset</button>
    <button type="submit" ng-disabled="searchForm.$invalid">Search</button>
  </div>
</form>

Как вы можете видеть, когда форма сбрасывается, она вызывает reset метод на $scope, Вот как выглядит весь контроллер:

angular.module('app').controller('mainController', function($scope) {
    $scope.resetCount = 0;

    $scope.reset = function(form) {
        form.$setPristine();
        form.$setUntouched();
        $scope.resetCount++;
    };

    $scope.search = function() {
        alert('Searching');
    };
});

Я зову form.$setPristine() а также form.$setUntouched, следуя совету из другого вопроса здесь о переполнении стека. Единственная причина, по которой я добавил счетчик, состояла в том, чтобы доказать, что код вызывается (что это такое).

Проблема в том, что даже после сброса формы сообщения проверки не исчезают. Вы можете увидеть полный код на Plunker. Вот скриншот, показывающий, что ошибки не исчезают:

Ошибки валидации

10 ответов

Решение

Я начал с комментария от @Brett и основывался на нем. У меня на самом деле есть несколько форм, и каждая форма имеет много полей (больше, чем только два, показанные). Поэтому я хотел общее решение.

Я заметил, что Angular form У объекта есть свойство для каждого элемента управления (input, select, textarea и т. д.), а также некоторые другие свойства Angular. Каждое из угловых свойств, тем не менее, начинается со знака доллара ($). В итоге я сделал это (включая комментарий для других программистов):

$scope.reset = function(form) {
    // Each control (input, select, textarea, etc) gets added as a property of the form.
    // The form has other built-in properties as well. However it's easy to filter those out,
    // because the Angular team has chosen to prefix each one with a dollar sign.
    // So, we just avoid those properties that begin with a dollar sign.
    let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);

    // Set each control back to undefined. This is the only way to clear validation messages.
    // Calling `form.$setPristine()` won't do it (even though you wish it would).
    for (let name of controlNames) {
        let control = form[name];
        control.$setViewValue(undefined);
    }

    form.$setPristine();
    form.$setUntouched();
};
      $scope.search = {areaCode: xxxx, phoneNumber: yyyy}

Структура всех моделей в вашей форме в одном месте, как показано выше, чтобы вы могли очистить его следующим образом:

      $scope.search = angular.copy({});

После этого вы можете просто вызвать это для сброса проверки:

      $scope.search_form.$setPristine();
      $scope.search_form.$setUntouched();
      $scope.search_form.$rollbackViewValue();

Кажется, нет простого способа сбросить ошибки $ в angular. Вероятно, лучшим способом было бы перезагрузить текущую страницу, чтобы начать с новой формы. В качестве альтернативы вы должны удалить все ошибки $ вручную с помощью этого сценария:

form.$setPristine(true);
form.$setUntouched(true);

// iterate over all from properties
angular.forEach(form, function(ctrl, name) {
  // ignore angular fields and functions
  if (name.indexOf('$') != 0) {
    // iterate over all $errors for each field        
    angular.forEach(ctrl.$error, function(value, name) {
      // reset validity
      ctrl.$setValidity(name, null);
    });
  }
});
$scope.resetCount++; 

Вы можете добавить флаг проверки и показать или скрыть ошибки в соответствии с их значением с помощью ng-if или же ng-show в вашем HTML. Форма имеет флаг $valid, который вы можете отправить своему контроллеру.

ng-if удалит или воссоздает элемент в DOM, а ng-show добавит, но не покажет (в зависимости от значения флага).

РЕДАКТИРОВАТЬ: Как указал Майкл, если форма отключена, то, как я указал, не будет работать, потому что форма никогда не отправляется. Обновил код соответственно.

HTML

<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
  <div>
    <label>
      Area Code
      <input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
    </label>

    <div ng-messages="searchForm.areaCode.$error">
      <div class="error" ng-message="pattern" ng-if="searchForm.areaCode.$dirty">The area code must be three digits</div>
    </div>
  </div>

  <div>
    <label>
      Phone Number
      <input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
    </label>

    <div ng-messages="searchForm.phoneNumber.$error">
      <div class="error" ng-message="pattern" ng-if="searchForm.phoneNumber.$dirty">The phone number must be seven digits</div>
    </div>
  </div>

  <br>
  <div>
    <button type="reset">Reset</button>
    <button type="submit" ng-disabled="searchForm.$invalid">Search</button>
  </div>
</form>

JS

$scope.search = function() {
    alert('Searching');
};

$scope.reset = function(form) {
     form.$setPristine();
     form.$setUntouched();
     $scope.resetCount++;
 };

Codepen с рабочим решением: http://codepen.io/anon/pen/zGPZoB

Следующее сработало для меня

let form = this.$scope.myForm;   
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
for (let name of controlNames) {
    let control = form [name];
    control.$error = {};
}

Вкратце: чтобы избавиться от ошибок ng-messages, необходимо очистить объект $error для каждого элемента формы.

Похоже, я должен сделать правильное поведение при перезагрузке. К сожалению, использование стандартного сброса не удалось. Я тоже не включаю библиотеку ui-event, Так что мой код немного отличается от вашего, но он делает то, что вам нужно.

<form name="searchForm" id="searchForm" ng-submit="search()">
  pristine = {{searchForm.$pristine}} valid ={{searchForm.$valid}}
  <div>
    <label>
      Area Code
      <input type="tel" required name="areaCode" ng-model="obj.areaCode" ng-pattern="/^([0-9]{3})?$/" ng-model-options="{ allowInvalid: true }">
    </label>

    <div ng-messages="searchForm.areaCode.$error">
      <div class="error" ng-message="pattern">The area code must be three digits</div>
      <div class="error" ng-message="required">The area code is required</div>
    </div>
  </div>

  <div>
    <label>
      Phone Number
      <input type="tel" required name="phoneNumber" ng-model="obj.phoneNumber" ng-pattern="/^([0-9]{7})?$/" ng-model-options="{ allowInvalid: true }">
    </label>

    <div ng-messages="searchForm.phoneNumber.$error">
      <div class="error" ng-message="pattern">The phone number must be seven digits</div>
      <div class="error" ng-message="required">The phone number is required</div>
    </div>
  </div>

  <br>
  <div>
    <button ng-click="reset(searchForm)" type="reset">Reset</button>
    <button type="submit" ng-disabled="searchForm.$invalid">Search</button>
  </div>
</form>

И JS:

$scope.resetCount = 0;
$scope.obj = {};
$scope.reset = function(form_) {
  $scope.resetCount++;
  $scope.obj = {};
  form_.$setPristine();
  form_.$setUntouched();
  console.log($scope.resetCount);
};

$scope.search = function() {
  alert('Searching');
};

Живой пример на jsfiddle.

Обратите внимание на директиву ng-model-options="{allowinvalid: true}", Используйте его обязательно, или до тех пор, пока поле ввода будет недействительным, значение модели не будет записано. Поэтому сброс не будет работать.

PS Значение объекта (areaCode, phoneNumber) для объекта упрощает очистку.

У меня была такая же проблема, и я попытался сделать решение Battmanz (принятый ответ).

Я уверен, что его ответ действительно хорош, но для меня это не сработало.

Я использую ng-модель для привязки данных и библиотеку угловых материалов для входных данных и директивы ng-message для сообщений об ошибках, поэтому, возможно, то, что я скажу, будет полезно только для людей, использующих ту же конфигурацию.

Я много смотрел на объект formController в javascript, на самом деле, как заметил battmanz, есть много $ angular функции, и, кроме того, есть имена ваших полей, которые являются объектом с некоторыми функциями в его полях.

Так что очищает вашу форму?

Обычно я вижу форму как объект json, и все поля связаны с ключом этого объекта json.

//lets call here this json vm.form
vm.form = {};

//you should have something as ng-model = "vm.form.name" in your view

Поэтому сначала для очистки формы я просто сделал обратный вызов для отправки формы:

vm.form = {};

И, как объяснено в этом вопросе, ng-сообщения не исчезнут с этим, это действительно плохо.

Когда я использовал решение battmanz, как он написал, сообщения больше не появлялись, но после отправки поля не оставались пустыми, даже если я написал

vm.form = {};

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

Так что я сделал?

На самом деле я просто очищаю поле (устанавливая привязку на {}), и использовал просто

form.$setPristine();
form.$setUntouched();

На самом деле это кажется логичным, так как привязка все еще здесь, значения в форме теперь пусты, и угловая директива ng-messages срабатывает только в том случае, если форма не тронута, так что я думаю, что это все-таки нормально.

Окончательный (очень простой) код таков:

function reset(form) {
        form.$setPristine();
        form.$setUntouched();
};

Большая проблема, с которой я столкнулся:

Только один раз, кажется, что обратный вызов где-то облажался, и почему-то поля не были пустыми (как будто я не нажимал кнопку отправки).

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

Я не знаю, является ли мой способ работы наилучшим или даже правильным, если у вас есть какие-либо критические замечания или предложения по поводу проблемы, с которой я столкнулся, пожалуйста, дайте мне знать, я всегда люблю выступать в angularJS.

Надеюсь, что это поможет кому-то и извините за плохой английский.

Далее к ответу @battmanz, но без использования синтаксиса ES6 для поддержки старых браузеров.

 $scope.resetForm = function (form) {

            try {
                var controlNames = Object.keys(form).filter(function (key) { return key.indexOf('$') !== 0 });

                console.log(controlNames);
                for (var x = 0; x < controlNames.length; x++) {
                    form[controlNames[x]].$setViewValue(undefined);
                }

                form.$setPristine();
                form.$setUntouched();
            } catch (e) {                
                console.log('Error in Reset');
                console.log(e);
            }

        };

Так что ни один из ответов не работал для меня полностью. Esp, очистка значения представления, поэтому я объединил все ответы, очистив значение представления, очистив ошибки и очистив выборку с помощью j-запроса (при условии, что поля ввода и имя совпадают с именем модели)

 var modelNames = Object.keys($scope.form).filter(key => key.indexOf('$') !== 0);
                    modelNames.forEach(function(name){
                        var model = $scope.form[name];
                        model.$setViewValue(undefined);
                        jq('input[name='+name+']').val('');
                        angular.forEach(model.$error, function(value, name) {
                          // reset validity
                          model.$setValidity(name, null);
                        });
                    });
                    $scope.form.$setPristine();
                    $scope.form.$setUntouched();

Вы можете передать свой объект loginForm в функцию ng-click="userCtrl.login(loginForm) и в вызове функции

this.login = function (loginForm){
  loginForm.$setPristine();
  loginForm.$setUntouched();
}
Другие вопросы по тегам