如果用户将输入值更改为初始值,则将表单重置为原始值

时间:2016-11-08 11:07:59

标签: angularjs forms

Angular在$dirty上有$pristineFormController个属性,可用于检测用户是否与表单进行了互动。这两个属性或多或少都是同一枚硬币的反面。

我想实现最简单的方法来实际检测表单何时仍然处于原始状态,尽管用户与它进行交互。表格可能有任何输入。例如,如果用户首先更改某些内容,然后将其更改回初始值,我希望我的表单再次为$pristine

对于文本输入,这可能不是那么明显,但我有一个复选框列表,用户可以在其中切换其中一些然后改变主意...我只希望用户能够保存实际变化。问题在于,无论用户是否与列表进行交互,表单都会变脏,无论用户是否重新切换相同的复选框,使整个表单恢复到最初的状态。

一种可能的方式我可以在每个复选框中保存默认值,并为每个复选框添加ngChange,每次都会检查所有默认值并调用$setPristine如果所有这些都有初始值。

但我想有更好,更简单的方法来做同样的事情。也许(ab)使用验证器甚至更巧妙的东西?

问题

在与之交互后检测表格是最原始的最简单方法是什么?

2 个答案:

答案 0 :(得分:0)

当你的控制器初始化时:

constructor() {
  super(arguments);

  this._copy = angular.copy(this._formModel);
}

然后你可以把手表放在模特身上。

this._$scope.$watch('this._formModel', (new, old) => {
  if (_.eq(this._copy, this._formModel)) {
    formObject.$setPristine();
  }
});

如果副本与模型相同,它仍然是原始的。

编辑:第二个选项是将ngChange添加到每个输入以调用控制器上的方法,然后执行与上面相同的过程。这仍然依赖于您在构造函数中复制原始(空白)模型。

<input ng-change="vm.noticeInputChange(t)" id="some_element" class="some_class" />

然后在控制器中:

noticeInputChange() {
  if (_.eq(this._copy, this._formModel)) {
    formObject.$setPristine();
  }
}

应该这样做,但正如已经指出的那样,$ watch可能会变得非常昂贵,具体取决于表单的大小。此外,正如这里有人指出的那样,_.eq()是lodash方法

答案 1 :(得分:0)

可以通过在ngModel内置指令中使用指令来完成,并观察模型值并在需要时对pristine进行更改。它比观看整个表格要便宜,但看起来仍然有点矫枉过正,而且我不确定大表现的表现。

  

注意:以下代码段不是此解决方案的最新版本,请查看 UPDATE 1 以获取最新的优化解决方案。

&#13;
&#13;
angular.module('app', [])
  .directive('ngModel', function() {
    return {
      restrict: 'A',
      require: ['ngModel', '^?form'],
      priority: 1000, // just to make sure it will run after the built-in
      link: function(scope, elem, attr, ctrls) {
        var
          rawValue,
          ngModelCtrl = ctrls[0],
          ngFormCtrl = ctrls[1],
          isFormValue = function(value) {
            return typeof value === 'object' && value.hasOwnProperty('$modelValue');
          };

        scope.$watch(attr.ngModel, function(value) {

          // store the raw model value
          // on initial state
          if (rawValue === undefined) {
            rawValue = value;
            return;
          }

          if (value == rawValue) {

            // set model pristine
            ngModelCtrl.$setPristine();

            // don't need to check if form is not defined
            if (!ngFormCtrl) return;

            // check for other named models in case are all pristine
            // sets the form to pristine as well
            for (key in ngFormCtrl) {
              var value = ngFormCtrl[key];
              if (isFormValue(value) && !value.$pristine) return;
            }

            // if haven't returned yet, means that all model are pristine
            // so then, sets the form to pristine as well
            ngFormCtrl.$setPristine();
          }
        });
      }
    };
  })
  .controller('myController', function($rootScope, $timeout) {

    var $ctrl = this;

    $ctrl.model = {
      name: 'lenny',
      age: 23
    };
    $timeout(function() {
      console.log('watchers: ' + $rootScope.$$watchersCount)
    }, 1000);
  });

angular.element(document).ready(function() {
  angular.bootstrap(document, ['app']);
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.js"></script>
<div ng-controller="myController as $ctrl">
  <form name="$ctrl.myForm" novalidate>
    <label>Name :
      <input name="test1" ng-model="$ctrl.model.name">
    </label>
    <label>age :
      <input name="test2" ng-model="$ctrl.model.age">
    </label>

    <label>Pristine: {{ $ctrl.myForm.$pristine }}</label>
    <div><pre>
  </pre>
    </div>
  </form>
</div>
&#13;
&#13;
&#13;

更新1

将观看系统更改为观看一次并删除额外的观察者。现在更改来自ngModelController的更改侦听器,并且观察者在第一个模型集上未绑定。正如控制台日志所注意到的那样,根目录上的观察者数量总是使观察者的数量增加一倍,通过这样做,观察者的数量保持不变。

&#13;
&#13;
angular.module('app', [])
  .directive('ngModel', function() {
    return {
      restrict: 'A',
      require: ['ngModel', '^?form'],
      priority: 1000,
      link: function(scope, elem, attr, ctrls) {
        var
          rawValue,
          ngModelCtrl = ctrls[0],
          ngFormCtrl = ctrls[1],
          isFormValue = function(value) {
            return typeof value === 'object' && value.hasOwnProperty('$modelValue');
          };

        var unbindWatcher = scope.$watch(attr.ngModel, function(value) {
          // set raw value 
          rawValue = value;

          // add a change listenner
          ngModelCtrl.$viewChangeListeners.push(function() {
            if (rawValue === undefined) {
              //rawValue = ngModelCtrl.$lastCommit;
            }
            if (ngModelCtrl.$modelValue == rawValue) {
              // set model pristine
              ngModelCtrl.$setPristine();

              // check for other named models in case are all pristine
              // sets the form to pristine as well
              for (key in ngFormCtrl) {
                var value = ngFormCtrl[key];
                if (isFormValue(value) && !value.$pristine) return;
              }
              ngFormCtrl.$setPristine();
            }
          });

          // unbind the watcher at the first change
          unbindWatcher();

        });


      }
    };
  })
  .controller('myController', function($rootScope, $timeout) {

    var $ctrl = this;

    $ctrl.model = {
      name: 'lenny',
      age: 23
    };
    $timeout(function() {
      console.log('watchers: ' + $rootScope.$$watchersCount)
    }, 1000);
  });

angular.element(document).ready(function() {
  angular.bootstrap(document, ['app']);
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.js"></script>
<div ng-controller="myController as $ctrl">
  <form name="$ctrl.myForm" novalidate>
    <label>Name :
      <input name="test1" ng-model="$ctrl.model.name">
    </label>
    <label>age :
      <input name="test2" ng-model="$ctrl.model.age">
    </label>

    <label>Pristine: {{ $ctrl.myForm.$pristine }}</label>
    <div><pre>
  </pre>
    </div>
  </form>
</div>
&#13;
&#13;
&#13;