AngularJS 表单验证:$dirty 值通过 ui.router 状态变化而变化

AngularJS form validation : $dirty value change through ui.router state changes

我的用例:

我有一个使用 ui-router 的多步骤表单,如下面的 plunkr 所示。我使用 ng-form 来验证 AngularJS 提供的信息,例如 $valid、$dirty 等

每次单击 "Next section" 按钮后,我都会将表单数据发送到服务器以便检索它,以防用户在完成之前 quits 表单。

如果用户提交了两次第一步,我只发送编辑过的数据(如果$dirty 值为真)。所有这些都不在 plunkr 中,我选择向您展示一个简单的表单,但我的表单可以包含一百个字段(单选框、复选框、输入、select 等)。

重现问题的步骤 (plunkr):

  1. 填写第 1 步并转到下一部分
  2. 选中 xbox 收音机并return点击数字进入第 1 步 myMultiStepForm.interests.xbox.$dirty = true
  3. 回到第2步 myMultiStepForm.interests.xbox.$dirty = false

为什么 $dirty 值变为 false?我猜这是因为 <ng-form> 再次显示并且所有验证数据都已重置。

有没有办法避免这种情况?或者也许 <ng-form> 以外的东西来处理字段子集的验证?

这是 plunkr : http://plnkr.co/edit/WclqVgiBvUXlsGdSCcj0?p=preview

我解决了,解决方案在下面的plunker中。

参考这个:

Note: the purpose of ngForm is to group controls, but not to be a replacement for the tag with all of its capabilities (e.g. posting to the server, ...).

根据 Angular Documentation for ngForm element.

我还想知道您为什么要使用许多 <ng-form> 元素。 这是您问题的唯一可能解决方案。

Plunker Modified

更新 1:

$dirty

的解释

$dirty 仅当 用户与当前范围内的特定元素交互 时才设置为真。

如果您对 true/false 问题如此挑剔

  • 建议您将 $dirty 替换为 $pristine

更新 2:

为什么再次向后导航时值不正确

当您从一个表单导航到另一个表单时,该特定元素的 ng-form 的 scope 被添加到父控制器,当再次访问相同的 ng-form 时它 覆盖 现有的。

当您 link 表单或其中的任何控制器时,它始终以 $pristine 开头。原因是像 formData.type 这样的模型只有一些值,angular 无法知道这些值是默认状态,来自服务器,还是先前用户交互的结果;它们是简单的 string 或没有附加此类元数据的东西。

要实现您想要的效果,您必须跨状态转换手动跟踪 $dirty 状态,并在需要时在表单上应用 $setDirty

这是一个简单的例子,将控制器添加到表单步骤页面,它在退出时保存表单状态(到共享服务实例,您也可以通过 resolve 添加它)并在建造。当前 formPage 是通过默认参数值注入的,因此同一控制器可用于所有步骤。

// router:

$stateProvider.state('form.interests', {
  url: '/interests',
  controller: 'FormStepController',  
  params: { formPage: 'interests'}
  templateUrl: 'form-interests.html'
})

// state

angular.value("formDirtyState", {});

// controller

angular.controller("FormStepController", function($scope, formDirtyState, $stateParams) {
  var formPage = stateParams.formPage;

  for(var formField in $scope.myMultiStepForm[formPage]) {
    if(formDirtyState[formPage] && formDirtyState[formPage][formField])
      $scope.myMultiStepForm[formPage][formField].$setDirty()
  }
    

  $scope.$on("$destroy", function() { 
    for(var formField in $scope.myMultiStepForm[formPage])
      formDirtyState[formField] = $scope.myMultiStepForm[formPage][formField].$dirty;
  })

})