Set AngularJS nested forms to submitted
Asked Answered
J

4

8

I've got a nested AngularJS form like so:

<form name="parentForm" ng-submit="submit()">
    <input name="parentInput" type="text">
    <ng-include src="childForm.html" ng-form="childForm"></ng-include>
    <button type="submit">Submit</submit>
</form>

And here's childForm.html

<input name="childInput" type="text">

For reasons unrelated to the question, I can't merge the parent and child forms - they need to be two separate files.

Now, when the user clicks the submit button, validation is correctly applied to both parentForm and childForm. However, only the parent form has it's $submitted flag set to true, which is problematic since I'm using it to trigger the display of certain error messages. I don't want the child form checking if the parent form is $submitted, since they're two separate files. The only option that's occurred to me is having the submit() method call $setSubmitted() on the child form, which is awkward since now the parent form needs to directly reference the child form. Is there a better way to set the child form's $submitted to true?

Judiciary answered 13/9, 2014 at 0:42 Comment(1)
how is that: "trigger the display of certain error messages"?Nucellus
P
6

As an extension of Meeker's solution, you could achieve the $broadcast implicitly by adding a watch to the parent form:

.directive('form', function() {
  return {
    restrict: 'E',
    require:  'form',
    link: function(scope, elem, attrs, formCtrl) {

      scope.$watch(function() {
        return formCtrl.$submitted;
      }, function(submitted) {
        submitted && scope.$broadcast('$submitted');
      });
    }
  };
})

.directive('ngForm', function() {
  return {
    restrict: 'EA',
    require:  'form',
    link: function(scope, elem, attrs, formCtrl) {

      scope.$on('$submitted', function() {
        formCtrl.$setSubmitted();
      }); 
    }
  };
})
Psychometry answered 9/5, 2015 at 10:44 Comment(1)
Note this only works if formCtrl.$submitted is changed. So it might not work with dynamic nested ng-form.Salford
C
4

There's an issue in angular's bug tracker for this https://github.com/angular/angular.js/issues/10071. One comment suggests this workaround in the meantime:

// sets all children ng-forms submitted (no such default functionality)
function setSubmitted(form) {
    form.$setSubmitted();
    angular.forEach(form, function(item) {
        if(item && item.$$parentForm === form && item.$setSubmitted) {
            setSubmitted(item);
        }
    });
}

// so ie. instead of scope.form.$setSubmitted(); use:    
setSubmitted(scope.form);

One problem with this approach is that any forms added dynamically will not copy their parent's initial state. I'm using this when adding forms dynamically:

function onNewChildForm (form) {
    if(form.$$parentForm && form.$$parentForm.$submitted) {
        setFormSubmitted(form);
    }
}
Collect answered 25/1, 2016 at 14:44 Comment(0)
F
2

This was my solution to this problem (and I bet someone can make this prettier)

I made an ngForm directive that attached a listener,

.directive('ngForm', function(){
    return {
        restrict: 'AE',
        require: 'form',
        link: function(scope,element,attrs,form){
            var parentForm = element.parent().controller('form');
            if(parentForm){
                scope.$on('parentFormSubmitted',function(event){
                    form.$setSubmitted();
                });
            }
        }
    };
})

Then in the controller of the parent form I execute this peice of code when the form is submitted

$scope.submit = function(){
  $scope.$broadcast('parentFormSubmitted');
}
Frit answered 30/9, 2014 at 21:15 Comment(0)
B
0

I initially used Scarlz' solution, but in my situation I had several nested ng-forms that are created/destroyed by ng-if.

Rather than using ng-show and dealing with possibly existing data, I modified Scarlz' solution to use the submit event instead of watching the form.$submittedproperty

  function ParentFormThatSubmits() {
    return {
      restrict: 'E',
      require: 'form',
      link: function(scope, elem, attrs, formCtrl) {
        elem.on('submit', function() {
          var submitted = formCtrl.$submitted;
          if(submitted) {
            scope.$broadcast('$submitted');
          }
        });
      }
    };
  }

  function ChildFormThatSubmits() {
    return {
      restrict: 'EA',
      require: 'form',
      link: function(scope, elem, attrs, formCtrl) {
        scope.$on('$submitted', function() {
          formCtrl.$setSubmitted();
          scope.$apply();
        });
      }
    };
  }

  angular.module('appModule')
    .directive('form', ParentFormThatSubmits)
    .directive('ngForm', ChildFormThatSubmits);
Boden answered 16/5, 2017 at 17:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.