Detach then append AngularJS form change validity status

混江龙づ霸主 提交于 2019-12-07 12:39:47

问题


You can test it in this jsFiddle: HERE (better is to see on new jsFiddle, see EDIT part of this post)

I think there is a bug in AngularJS or at least not an expected result. If i detach a form then re-append it, it's class ng-invalid switch to ng-valid on re-append it to the DOM. This has for consequence to enable the submit button of the form even data aren't valid. Of course, i was expecting that validity status didn't switch.

I think it's a angular bug, but maybe a jquery one. I could use jquery to check on append if form was valid or not and then forcing form class but it's seems not working as a valid form get then the status of invalid. It's quite weird as i don't know any other workaround without using kind of data to saved status form before detaching it.

So anyone has already encountered this problem? Is there any method (if possible using AngularJS directive) to get rid of this bug?

PS: i need to detach form (and any other elements) in a single page web application to keep DOM as clean as possible.

EDIT

I've done a new jsFiddle that is illustrating more my problem, detaching content on internal site navigation: http://jsfiddle.net/EWVwa/

UPDATE

I come to this temporary solution (thanks to 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");        
      });
    }
  };
});

This need more testing regarding multiple inputs validation. I'll certainly updating answer when all tests will be done.


回答1:


The problem happens because input directive removes itself from the form controls when the element is removed from DOM. As it doesn't link your ngModel and form controller again, your input is not being considered by the form anymore.

You basically have two three options:

  • change element visibility instead of removing it
  • (prefer the one below) exposing a "relink" function that would re-add it to the original form
  • triggering a custom event on all controls so they can relink theirselves

Changing element visibility means you will have unnecessary DOM elements in DOMTree. This is not quite bad, as you are keeping a reference to the $compile element anyway, so it will yet participate the $digest cycles and "DOM" modifications.

(After thinking for a while, the new solution is slightly better than this one, so don't expose a relinking function) Exposing a relink function is quite weird (although functional) and this is not the most reliable of the solutions. One way to achieve it consists in requiring form controller (require: ['ngModel', '^?form']) and binding a relinking function to the element's data:

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

And when you add the element to the screen again, you gonna have to call all your controls relink function:

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

See a example here.

It's not quite reliable, but might work for your case.

Triggering a custom event is pretty much the same as the previous, but you would dispatch a custom event on all elements that should relink theirselves. This is way more organized, but still not quite reliable, because the form and other links might also have been broken (again, should sufice your case). Basically listen to the custom event on your directive:

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

And after changing to the form, just trigger the custom event on all controls:

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

The reason why this one is better is that the directive still decides when to relink the component, and the event is kind of "generic". Here is a working plunker.

As a last effort, you could override jQuery.fn.append and trigger the custom event on all element children recursively (this is what Angular does when removing elements). This is the most organized, but it would impact yoour performance on ALL append calls.



来源:https://stackoverflow.com/questions/16079843/detach-then-append-angularjs-form-change-validity-status

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!