然后分离附加AngularJS表单更改有效性状态

时间:2013-04-18 09:57:19

标签: javascript jquery angularjs angularjs-directive

你可以在这个jsFiddle中测试它:HERE(更好的是看新的jsFiddle,参见 EDIT 这篇文章的一部分)

我认为AngularJS中存在一个错误,或者至少没有预期的结果。 如果我分离表单然后重新附加它,它的类ng-invalid切换到ng-valid重新附加到DOM。因此,即使数据无效,也可以启用表单的提交按钮。 当然,我期待有效状态不会改变。

我认为这是一个有角度的错误,但也许是一个jquery错误。 如果表单有效,我可以使用jquery检查append,然后强制表单类,但它似乎不能作为有效表单获取然后无效状态。这很奇怪,因为我不知道任何其他解决方法而不使用各种数据来保存状态表单,然后再将其分离。

所以任何人都已经遇到过这个问题? 是否有任何方法(如果可能使用AngularJS指令)来摆脱这个错误?

PS:我需要在单页面Web应用程序中分离表单(和任何其他元素),以保持DOM尽可能干净。

修改

我做了一个新的jsFiddle,它解释了我的更多问题,在内部网站导航上分离内容:http://jsfiddle.net/EWVwa/

更新

我来到这个临时解决方案(感谢CaioToOn)

http://plnkr.co/edit/KIgMz2

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';
});


app.directive('customValidation', function() {
  return {
    require: ['ngModel', '^?form'],
    link: function(scope, element, attr, ctrls) {
      console.log(ctrls);
      var ngModelCtrl = ctrls[0],
          formCtrl = ctrls[1];


      ngModelCtrl.$parsers.push(function(viewValue) {
        if (viewValue === 'test') {
          ngModelCtrl.$setValidity('name', true);
          formCtrl.$setValidity('name', true);
          return viewValue;
        } else {
          ngModelCtrl.$setValidity('name', false);
          formCtrl.$setValidity('name', false);
          return undefined;
        }
      });


      // custom event
      element.bind('$append', function() {
        formCtrl && formCtrl.$addControl(ngModelCtrl);
        /*** TEST for to keep form's validation status ***/
        formCtrl.$setValidity('name', ngModelCtrl.$valid);
        //ngModelCtrl.$setValidity('name', ngModelCtrl.$valid);
        console.log(formCtrl.$valid);
      });
      //binding on element, not scope. 
      element.bind('$destroy', function() {
        console.log("gone haven");        
      });
    }
  };
});

这需要更多关于多输入验证的测试。当所有测试都完成时,我肯定会更新答案。

1 个答案:

答案 0 :(得分:3)

问题发生是因为表单中的input指令removes本身控制从DOM中删除元素的时间。由于它不会再次链接您的ngModel和表单控制器,因此表单不再考虑您的输入。

你基本上有两个三个选项:

  • 更改元素可见性而不是删除它
  • (更喜欢下面的那个)暴露“重新链接”功能,将其重新添加到原始表单
  • 在所有控件上触发自定义事件,以便他们可以重新链接自己

更改元素可见性意味着您将在DOMTree中拥有不必要的DOM元素。这并不是很糟糕,因为你总是保留对$ compile元素的引用,所以它还会参与$digest周期和“DOM”修改。

经过一段时间的考虑,新解决方案略好于此,所以不要公开重新链接功能 公开重新链接功能 非常奇怪(虽然功能齐全),但这不是最可靠的解决方案。实现它的一种方法是要求表单控制器(require: ['ngModel', '^?form'])并将重新链接函数绑定到元素的数据:

element.data('relink', function(){
  formCtrl && formCtrl.$addControl(ngModelCtrl);
});

当您再次将元素添加到屏幕时,您将不得不调用所有控件重新链接功能:

$('.controls').data('relink')();

查看示例here

它不太可靠,但可能适用于您的情况。

触发自定义事件与之前的事件非常相似,但您会在所有应重新链接的元素上发送自定义事件。这种方式更有条理,但仍然不太可靠,因为表单和其他链接也可能已被破坏(同样,应该会使您的情况更糟)。基本上听你的指令上的自定义事件:

element.bind('$append', function(){
  formCtrl && formCtrl.$addControl(ngModelCtrl);
});

更改为表单后,只需在所有控件上触发自定义事件:

$('.control').triggerHandler('$append');

这个更好的原因是指令仍然决定何时重新链接组件,并且事件是一种“通用”。 Here is a working plunker

作为最后的努力,您可以覆盖jQuery.fn.append并以递归方式触发所有元素子节点上的自定义事件(这是删除元素时的Angular does)。这是最有条理的,但它会影响所有append次来电的性能。