【问题标题】:Reset form to pristine state (AngularJS 1.0.x)将表单重置为原始状态(AngularJS 1.0.x)
【发布时间】:2012-09-18 04:59:29
【问题描述】:

AngularJS 1.1.x 的路线图上有一个将表单字段重置为原始状态(重置脏状态)的功能。不幸的是,当前稳定版本中缺少这样的功能。

对于 AngularJS 1.0.x,将所有表单字段重置为其初始原始状态的最佳方法是什么?

我想知道这是否可以通过指令或其他简单的解决方法来解决。我更喜欢无需接触原始 AngularJS 源代码的解决方案。为了澄清和演示这个问题,一个指向 JSFiddle 的链接。 http://jsfiddle.net/juurlink/FWGxG/7/

所需功能在路线图 - http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html
功能请求 - https://github.com/angular/angular.js/issues/856
提出的解决方案拉取请求 - https://github.com/angular/angular.js/pull/1127

更新了可能的解决方法

足够好的解决方法?

我刚刚发现我可以重新编译 HTML 部分并将其放回 DOM。它可以工作,并且可以作为临时解决方案,但也正如 cmets 中提到的@blesh:

控制器只能用于业务逻辑,不能用于 DOM!

<div id="myform">
  <form class="form-horizontal" name="form">
  </form>
</div>

在我的控制器中 resetForm():

  • 保存原始未修改的 HTML
  • 重新编译保存的原始 HTML
  • 从 DOM 中移除当前表单
  • 将新编译的模板插入到 DOM 中

JavaScript:

var pristineFormTemplate = $('#myform').html();
$scope.resetForm = function () {
    $('#myform').empty().append($compile(pristineFormTemplate)($scope));
}

【问题讨论】:

  • Protip:控制器只能用于业务逻辑,不能用于 DOM 操作。 DOM 操作应该通过指令进行。 See "Using Controllers Correctly" here
  • 确实,这是一个非常臭的黑客。
  • form.$setPristine() 登陆1.1.1
  • 不费吹灰之力,我简单地从 1.1.x 分支复制了 $setPristine() 方法和一些小的依赖项(即controls 数组)。效果很好。

标签: angularjs


【解决方案1】:

您的表单字段应该链接到 $scope 中的变量。您可以通过重置变量来重置表单。它可能应该是像 $scope.form 这样的单个对象。

假设您有一个简单的用户表单。

app.controller('Ctrl', function Ctrl($scope){
  var defaultForm = {
    first_name : "",
    last_name : "",
    address: "",
    email: ""
  };
  $scope.resetForm = function(){
    $scope.form = defaultForm;
  };
});

只要您的 html 看起来像这样,这将非常有用:

<form>
  <input ng-model="form.first_name"/>
  <input ng-model="form.last_name"/>
  <input ng-model="form.address"/>
  <input ng-model="form.email"/>
  <button ng-click="resetForm()">Reset Form</button>
</form>

也许我不明白这里的问题,所以如果这不能解决你的问题,你能解释一下具体原因吗?

【讨论】:

  • 添加了指向 JSFiddle 示例的链接以演示该问题。清除输入不是问题。问题是清除表单域的“原始”状态。
  • 由于defaultForm 是一个对象引用,这可能无法按预期工作。
  • 对,更好用:$scope.form = angular.copy(defaultForm);
【解决方案2】:

编辑...我正在删除我的旧答案,因为它还不够。

实际上我自己也遇到了这个问题,这是我的解决方案:我为 angular.js 做了一个扩展方法。我通过遵循 $scope.form.$setValidity() 正在做的一些事情来做到这一点(相反)......

Here's a plnkr demo of it in action

这是我制作的辅助方法。这是一个 hack,但它有效:

angular.resetForm = function (scope, formName, defaults) {
    $('form[name=' + formName + '], form[name=' + formName + '] .ng-dirty').removeClass('ng-dirty').addClass('ng-pristine');
    var form = scope[formName];
    form.$dirty = false;
    form.$pristine = true;
    for(var field in form) {
      if(form[field].$pristine === false) {
        form[field].$pristine = true;
      }
      if(form[field].$dirty === true) {
        form[field].$dirty = false;
      }
    }
    for(var d in defaults) {
      scope[d] = defaults[d];
    }
};

希望这对某人有所帮助。

【讨论】:

  • 还没有。我将 $pristine/$dirty 状态用于#1。隐藏每个输入元素的初始必填字段消息和 #2 测试表单是否仍处于初始状态。表单被重用(不刷新页面),所以重置 $pristine/$dirty 状态对于检测变化很重要。它不仅仅是为了视觉呈现,所以我认为我不能只用样式类来解决这个问题。
  • 为什么不使用 form.element.$error.required 和 ngShow 来显示或隐藏必填字段消息?还有一个布尔值来测试表单是否处于原始状态?
  • 另外,看看这个问题,这是一个关于你所看到内容的公开请求:github.com/angular/angular.js/issues/856
  • 最后,就目前而言,您可能不得不使用一种解决方法。
  • 链接到所描述功能的拉取请求:github.com/angular/angular.js/pull/1127
【解决方案3】:

没有解决方法的解决方案

我想出了一个解决方案,它使用 AngularJS 而没有任何解决方法。这里的技巧是使用 AngularJS 的能力来拥有多个具有相同名称的指令。

正如其他人提到的那样,实际上有一个拉取请求 (https://github.com/angular/angular.js/pull/1127) 进入了允许重置表单的 AngularJS 1.1.x 分支。对此拉取请求的提交会更改 ngModel 和 form/ngForm 指令(我本来想添加一个链接,但 Stackoverflow 不希望我添加两个以上的链接)。

我们现在可以定义自己的 ngModel 和 form/ngForm 指令,并使用拉取请求中提供的功能扩展它们。

我已经将这些指令封装在一个名为 resettableForm 的 AngularJS 模块中。您所要做的就是将此模块包含到您的项目中,并且您的 AngularJS 版本 1.0.x 在这方面表现得就像它是 Angular 1.1.x 版本一样。

''一旦你更新到 1.1.x,你甚至不需要更新你的代码,只需删除模块就可以了!''

该模块还通过了所有添加到 1.1.x 分支的表单重置功能测试。

您可以在我创建的 jsFiddle (http://jsfiddle.net/jupiter/7jwZR/1/) 中的示例中看到该模块的工作原理。

第 1 步:在项目中包含 resettableform 模块

(function(angular) {

// Copied from AngluarJS
function indexOf(array, obj) {
  if (array.indexOf) return array.indexOf(obj);

  for ( var i = 0; i < array.length; i++) {
    if (obj === array[i]) return i;
  }
  return -1;
}

// Copied from AngularJS
function arrayRemove(array, value) {
  var index = indexOf(array, value);
  if (index >=0)
    array.splice(index, 1);
  return value;
}

// Copied from AngularJS
var PRISTINE_CLASS = 'ng-pristine';
var DIRTY_CLASS = 'ng-dirty';

var formDirectiveFactory = function(isNgForm) {
    return function() {
        var formDirective = {
            restrict: 'E',
            require: ['form'],
            compile: function() {
                return {
                    pre: function(scope, element, attrs, ctrls) {
                        var form = ctrls[0];
                        var $addControl = form.$addControl;
                        var $removeControl = form.$removeControl;
                        var controls = [];
                        form.$addControl = function(control) {
                            controls.push(control);
                            $addControl.apply(this, arguments);
                        }
                        form.$removeControl = function(control) {
                            arrayRemove(controls, control);
                            $removeControl.apply(this, arguments);
                        }
                        form.$setPristine = function() {
                            element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
                            form.$dirty = false;
                            form.$pristine = true;
                            angular.forEach(controls, function(control) {
                                control.$setPristine();
                            });
                        }
                    },
                };
            },
        };
        return isNgForm ? angular.extend(angular.copy(formDirective), {restrict: 'EAC'}) : formDirective;
    };
}
var ngFormDirective = formDirectiveFactory(true);
var formDirective = formDirectiveFactory();
angular.module('resettableForm', []).
    directive('ngForm', ngFormDirective).
    directive('form', formDirective).
    directive('ngModel', function() {
        return {
            require: ['ngModel'],
            link: function(scope, element, attrs, ctrls) {
                var control = ctrls[0];
                control.$setPristine = function() {
                    this.$dirty = false;
                    this.$pristine = true;
                    element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
                }
            },
        };
    });
})(angular);

第 2 步:在控制器上提供重置模型的方法

请注意,重置表单时必须重置模型。在您的控制器中,您可以编写:

var myApp = angular.module('myApp', ['resettableForm']);

function MyCtrl($scope) {
    $scope.reset = function() {
        $scope.form.$setPristine();
        $scope.model = '';
    };
}

第 3 步:在 HTML 模板中包含此方法

<div ng-app="myApp">
<div ng-controller="MyCtrl">
<form name="form">
    <input name="requiredField" ng-model="model.requiredField" required/> (Required, but no other validators)
    <p ng-show="form.requiredField.$errror.required">Field is required</p>
    <button ng-click="reset()">Reset form</button>
</form>
<p>Pristine: {{form.$pristine}}</p>
</div>
</dvi>

【讨论】:

  • 优雅的解决方案,效果很好。我喜欢这样的前向兼容修复。干得好。
  • 效果很好。非常感谢您的努力。我正在使用 AngularJS 1.0..7
【解决方案4】:

我认为值得一提的是,在更高版本的 Angular(例如 1.1.5)中,您可以在表单上调用 $setPristine

$scope.formName.$setPristine(true)

这也会将所有表单控件设置为原始状态。

FormController.$setPristine

【讨论】:

  • @bluehallu,那么应该在哪里调用呢?指令的链接函数?
  • 不起作用:Cannot read property '$setPristine' of undefined。实际上,当你把它放在$timeout(function () { ... }); 中时,我想它必须是异步的。
  • @bluehallu 是的,我也很想知道!
【解决方案5】:

在这里,我找到了将 from 置于原始状态的解决方案。

var def = {
    name: '',
    password: '',
    email: '',
    mobile: ''
};

$scope.submited = false;

$scope.regd = function (user) {
    if ($scope.user.$valid) {
        $http.post('saveUser', user).success(function (d) {
            angular.extend($scope.user, def);
            $scope.user.$setPristine(true);
            $scope.user.submited = false;
        }).error(function (e) {});
    } else {
        $scope.user.submited = true;
    }
};

只需写 angular.extends(src,dst) ,这样你的原始对象只是扩展了空白对象,它将显示为空白,其余都是默认的。

【讨论】:

  • 请编辑您的答案并格式化代码以使其可读
【解决方案6】:

使用外部指令和大量 jquery

app.controller('a', function($scope) {
    $scope.caca = function() {
        $scope.$emit('resetForm');
    }
});

app.directive('form', function() {
    return {
        restrict: 'E',
        link: function(scope, iElem) {
            scope.$on('resetForm', function() {
                iElem.find('[ng-model]').andSelf().add('[ng-form]').each(function(i, elem) {
                    var target = $(elem).addClass('ng-pristine').removeClass('ng-dirty');
                    var control = target.controller('ngModel') || target.controller('form');
                    control.$pristine = true;
                    control.$dirty = false;
                });
            });
        }
    };
});

http://jsfiddle.net/pPbzz/2/

【讨论】:

    【解决方案7】:

    简单的方法:只需将表单传递给控制器​​函数。 this 引用了表单“myForm”下面,相当于$scope。

    <div ng-controller="MyController as mc">
        <ng-form name="myform">
            <input ng-model="mc.myFormValues.name" type="text" required>
            <button ng-click="mc.doSometing(this.myform)" type="submit" 
                    ng-disabled="myform.$invalid||myform.$pristine">Do It!</button>
        </ng-form>
    </div>
    

    控制器:

    function MyController(MyService) {
        var self = this;
        self.myFormValues = {
            name: 'Chris'
        };
        self.doSomething = function (form) {
            var aform = form;
            MyService.saveSomething(self.myFromValues)
                .then(function (result) {
                    ...
                    aform.$setPristine();
                }).catch(function (e) {
                    ...
                aform.$setDirty();
            })
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-27
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      相关资源
      最近更新 更多