当从DOM中删除违规元素时,无效表单不会重新计算其有效性

时间:2013-01-18 21:52:15

标签: jquery forms dom validation angularjs

我在其中一个指令中使用ctrl.$setValidity来使表单无效。但是,有一种驻留在其他地方的方法可以在不同的条件下从DOM中删除该元素。当我使用$setValidity使表单无效然后删除有问题的元素时,会发生什么?表单仍然无效,而我希望它根据新的Inputfield集重新计算其有效性。

请注意,我不只是在寻找ctrl.$setValidity true,(表单中的其他输入字段可能有效也可能无效),我只是希望重新计算表单。

以下是指令代码:

 app.directive('dir', function() {
   return {
      restrict: 'C',
      require: "ngModel",
      link: function(scope, element, attrs, ctrl) {
      someValue = scope.someValue;
      someField = element.find(".some-class");

      monitorField = function(newValue, oldValue){
        console.log("whee");
        scope.someFunction(newValue);
        if(newValue =="Invalidate NOW!"){
        ctrl.$setValidity('someClass', false);      
        } else if (oldValue == "Invalidate NOW!"){
           angular.element(document.querySelector('.some-class')).remove();
        } else {
          ctrl.$setValidity('someClass', true); 
       }
    }  
    scope.$watch("someValue", monitorField, true);
   }
 }
});

其中包括:

   <FORM class="dir" ng-model="someValue">
     <INPUT type="text" class="some-class" ng-model="someValue"/>
     <INPUT type="text" class="some-other-class"/>
   </FORM>

(是的,这是一个有点人为的例子)。

问题转载于此:http://jsfiddle.net/kTuAY/

在我的实际代码中,我根据一个对象数组填充输入字段,这些对象通过Service填充,并通过array.splice删除元素。 jsfiddle中给出的例子仅仅是为了简单起见。

另一个有趣的失败条件可以在这个小提琴中看到:

http://jsfiddle.net/JACAv/

具体来说,如果其中一个输入依赖于值与另一个的非冲突,并且因此无效,那么其他字段值改变之后,有效性仍然不正确。

上班时临时不太正常的小提琴:

http://jsfiddle.net/yQpHL/

谢谢!

3 个答案:

答案 0 :(得分:2)

删除链接到模型部分的DOM元素应该通过删除模型指向的$ scoped对象的一部分来完成(如果可能)。想想基本的ngRepeat,如果从数组中删除一个项目,它会从DOM中删除一个元素。您的表单应该以相同的方式工作。这样可以防止这种情况发生。所有验证信息都包含在$ scope中的模型中......例如:$scope.myForm.model.$error.required。如果删除模型所指向的$ scope属性或数组项(例如$scope.foo$scope.items.splice(2,1),Angular将知道(可能在下一个$摘要期间)去除模型以及这是验证信息。

如果删除控制器或指令中的项目无关紧要,但应该从$ scope中删除它,然后需要为UI进行摘要并验证更新。

我希望有所帮助,因为我觉得我偶然发现了我想说的话......

答案 1 :(得分:0)

我认为问题不在指令中,而是在“其他”代码中

如果在对“本机”事件的响应中完成DOM元素移除 - 即拖放事件,ajax事件或某事,则角度框架将不知道自己运行重新计算。在完成更新后的代码中,您必须在适当的范围内调用$ scope。$ apply()。

哦,我看到你的小提琴......好吧。如何让生活变得更轻松 - 而不是删除你的DOM元素为什么不用ng-show属性来装饰它并让角度照顾其余的?

答案 2 :(得分:0)

我最终解决这个问题的方式是:

  1. 在数组ng-repeated上放置一个单独的监视表达式。
  2. 在该监视表达式中,构造输入字段中当前存在的与相关指令匹配的所有值的散列。
  3. 使用.val()强制重置这些字段的值,然后在内部字段上调用$compile以重新触发内部监视表达式
  4. 在函数中添加控制器上的输入字段值(在ctrl.$setValidity之前从内部指令调用的函数:
    • 验证输入值的次数是否为1
    • 重置ng-repeated结束的数组以包含哈希值

好处:

  • 删除元素后,外部监视(整个数组上的监视)将触发,重新计算散列值,重置每个字段的值并重新验证
  • 当检测到碰撞时,它会正确冒泡,将所有碰撞字段标记为无效,当删除碰撞时,所有字段都会重新验证
坏事:
  • 每次数组更改时,在每个字段上调用$ compile都是计算效率低下的
  • 由于内部监视表达式将在值更改时触发一次,并且在外部监视表达式调用compile时触发一次,实际上$digest的数量将加倍

我会在清理代码后立即编写一个代码示例。