Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

$submitted is not set on sub ng-form #10071

Closed
luboid opened this issue Nov 15, 2014 · 20 comments
Closed

$submitted is not set on sub ng-form #10071

luboid opened this issue Nov 15, 2014 · 20 comments

Comments

@luboid
Copy link

luboid commented Nov 15, 2014

Hi,

I have <form> with inner <ng-form>
when form is submitted inner <ng-form>.$submitted is not set to true only parent

@pkozlowski-opensource
Copy link
Member

I don't think we should expect the inner ngForm to behave exactly like the <form> thing. I guess we could fix this one but would be cool to know what is your real-life use-case?

@luboid
Copy link
Author

luboid commented Nov 15, 2014

Hi

I create wizard interface,
there are two directives, which I use to show errors after form is submitted

        .directive('bkErrorText', function () {
            return {
                require: '^form',
                template: '<span></span>',
                restrict: 'E',
                link: function ($scope, $element, $attrs, ctrl) {
                    var inputCtrl = ctrl[$attrs.bkInputName];
                    if (!inputCtrl) {
                        throw 'Can\'t find input ' + $attrs.bkInputName + ' into form.';
                    }

                    function showErrorText(newValue, oldValue) {
                        if (inputCtrl.$invalid && (ctrl.$submitted || ctrl.$$parentForm.$submitted)) {
                            var i, key, e, etext = [], error = inputCtrl.$error;
                            for (i in error) {
                                if (error[i]) {
                                    key = 'bkMsg' + angular.uppercase(i.charAt(0)) + i.substr(1);
                                    e = $attrs[key] || (key + ' no message text!');
                                    etext.push(e);
                                    etext.push('&nbsp;');
                                }
                            }
                            $element.html(etext.join(''));
                            $element.removeClass('hidden');
                        }
                        else {
                            if (!$element.hasClass('hidden')) {
                                $element.addClass('hidden');
                                $element.html('');
                            }
                        }
                    };

                    $scope.$watchCollection(function () { return inputCtrl.$error; }, showErrorText);
                    $scope.$watch(function () {
                        return inputCtrl.$invalid && (ctrl.$submitted || ctrl.$$parentForm.$submitted);
                    }, showErrorText);
                }
            };
        })
        .directive('bkHasError', function () {
            return {
                require: '^form',
                restrict: 'A',
                scope: {
                    bkHasError: '@',
                    bkHasErrorClass: '@'
                },
                link: function ($scope, $element, $attrs, ctrl) {
                    var cssClass = $scope.bkHasErrorClass || 'has-error',
                        inputCtrl = ctrl[$scope.bkHasError];

                    $scope.$watch(function () {
                        return inputCtrl.$invalid && (ctrl.$submitted || ctrl.$$parentForm.$submitted);
                    }, function (newValue, oldValue) {
                        if (newValue) {
                            $element.addClass(cssClass);
                        }
                        else {
                            $element.removeClass(cssClass);
                        }
                    });
                }
            };
        })
<form class="form-horizontal" role="form" novalidate="novalidate" ng-submit="submitForm()">
    <tabset>
        <tab select="tabs['common']=true" deselect="tabs['common']=false" active="tabs['common']">
            <tab-heading ng-class="{'text-danger':tabsValid['common']===false}">
                Общи
            </tab-heading>
            <ng-form name="common" bk-submit-valid="tabsValid['common']">
                <div class="form-group" bk-has-error="amountBuy">
                    <label class="col-md-2 control-label" for="amountBuy">amountBuy</label>
                    <div class="col-md-3">
                        <input class="form-control bk-number-lg" id="amountBuy" name="amountBuy" type="text"
                               ng-model="model.amountBuy"
                               bk-number="15,2"
                               bk-number-range="0.01,999999.99"
                               ng-model-options="{ debounce: 300 }" />
                        <bk-error-text class="help-block"
                                       bk-input-name="amountBuy"
                                       bk-msg-required="Data is required ..."
                                       bk-msg-number="Number(15,2) ..."
                                       bk-msg-number-range="Number range from 0.01 to 999 999.99 ..." />
                    </div>
                </div>
                <!--
                    others input fields
                -->
            </ng-form>
        </tab>
        <!--
            others tabs
        -->
    </tabset>
    <div class="col-md-offset-2 col-md-6">
        <button class="btn btn-default" type="button" ng-click="prevTab()">Prev</button>
        <button class="btn btn-default" type="button" ng-click="nextTab()">Next</button>
        <button class="btn btn-primary" type="submit" bk-disable-pristine>Save</button>
    </div>
</form>

ckniffen pushed a commit to ckniffen/angular.js that referenced this issue Feb 10, 2015
…tted.

FormController will now call $setSubmitted on its child forms when it is submitted.

Closes angular#10071
ckniffen pushed a commit to ckniffen/angular.js that referenced this issue Mar 5, 2015
…tted.

FormController will now call $setSubmitted on its child forms when it is submitted.

Closes angular#10071
ckniffen pushed a commit to ckniffen/angular.js that referenced this issue Mar 5, 2015
@awerlang
Copy link

Another approach is using css:

.ng-submitted .ng-invalid {
.... styles styles styles ...
}

or looping through all parent forms:

while (form) form = form.$$parentForm;

@kwypchlo
Copy link
Contributor

👍 I would like to see that too

My rl scenario is that I have a form where I have directive that uses ng-form and inside it I also have another directive with some input fields. That second (most inner) directive requires form controller in link function and it is the form controller from the ng-form directive. I have validation messages showing on submitted form but the only form that gets $submitted flag is the most outer form. The ng-form controller has $submitted set to false and the validation messages are not shown.

@awerlang
Copy link

@kwypchlo does the css I posted above solve your issue?

@stryju
Copy link

stryju commented May 21, 2015

real life use-case:

some form:

<form ng-form="form1" ng-submit="save(form1)">
  <custom-fieldset model="model.firstname" required></custom-fieldset>
  <custom-fieldset model="model.lastname" required></custom-fieldset>
  <button>save</button>
</form>

custom-fieldset.html:

<fieldset ng-form="form">
  <input name="name" required ng-model="ctrl.model">
  <ng-messages role="alert"
    for="(form.$submitted || form.name.$touched) &amp;&amp; form.name.$error">
    <ng-message when="required">please enter a name</ng-message>
  </ng-messages>
</fieldset>

@ckniffen
Copy link

@stryju This is similar to the use case I had.

@kwypchlo
Copy link
Contributor

@awerlang nope, I need this functionality in js, inside my directive. The workaround with $$parentForm may work but sure doesn't look elegant :)

ckniffen pushed a commit to ckniffen/angular.js that referenced this issue May 26, 2015
ckniffen pushed a commit to ckniffen/angular.js that referenced this issue Sep 13, 2015
ckniffen pushed a commit to ckniffen/angular.js that referenced this issue Sep 13, 2015
ckniffen pushed a commit to ckniffen/angular.js that referenced this issue Sep 23, 2015
ckniffen pushed a commit to ckniffen/angular.js that referenced this issue Sep 23, 2015
@ashleahhill
Copy link

👍 for including this.

I have a setup similar to @stryju 's above. I added a watch to the child forms for now to watch the parent form's $submitted property, but It does feel hacky.

@fromi
Copy link

fromi commented Nov 12, 2015

I ran into this issue as well. I have a subform that I reuse in many forms, there it is excluded to refer to parent form name to know if it was submitted or not.

For example, when I submit my user form, obviouly my subform for user address is submitted as well. However, userForm.$submitted becomes true, but userForm.addressForm.$submitted stays to false.

user-form.html:

<form name="userForm" novalidate>
    <div ng-class="{ 'has-error' : userForm.$submitted &amp;&amp; userForm.firstName.$invalid}">
        <label>First name</label>
        <input type="text" ng-model="user.firstName" name="firstName" required>
    </div>
    <div ng-class="{ 'has-error' : userForm.$submitted &amp;&amp;  userForm.lastName.$invalid}">
        <label>Last name</label>
        <input type="text" ng-model="user.lastName" name="lastName" required>
    </div>
    <fieldset ng-form="addressForm" class="address" ng-include="'address/address-fields.html'"></fieldset>
</form>

address-fields.html:

<div ng-class="{ 'has-error' : addressForm.$submitted &amp;&amp;  addressForm.postcode.$invalid}">
    <label>Postcode</label>
    <input type="text" ng-model="address.postcode" ng-required="true" name="postcode">
</div>
<div ng-class="{ 'has-error' : addressForm.$submitted &amp;&amp;  addressForm.city.$invalid}">
    <label>City</label>
    <input type="text" ng-model="address.city" ng-required="true" name="city">
</div>
<div ng-class="{ 'has-error' : addressForm.$submitted &amp;&amp;  addressForm.country.$invalid}">
    <label>Country</label>
    <input type="text" ng-model="address.country" ng-required="true" name="country">
</div>

@karolwebsky
Copy link

+1

@santhony7
Copy link

Yes please!

@karolwebsky
Copy link

quick workaround I've found somewhere and modified:

// 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);

@stevemao
Copy link
Contributor

A workaround: http://stackoverflow.com/a/30138961/2075423

Note this only works if formCtrl.$submitted is changed. So it might not work with dynamic nested ng-form.

@bolemeus
Copy link

bolemeus commented Mar 9, 2016

I have thesame issue. I made the following workaround based on awerlang's suggestion...

In my directive.

ctrl.isSubmitted = function() {
    var form = ctrl.form;
    while (!!form) {
        if (form.$submitted) return true;
        form = form.$$parentForm;
    }
    return false;
};

In the view...

<div class="has-feedback" ng-class="{'has-error': ctrl.isSubmitted() && ctrl.form[ctrl.name].$invalid}">
    ...
</div>

I do agree that this feels a bit hacky though, but it works...
The css rules would work as well I think, but the has-error class is defined in bootstrap, and I don't really want to duplicate the css from bootstrap for a custom css rule. That can get messy when migrating to newer bootstrap versions.

@pbrain19
Copy link

will this issue be resolved before Trump takes office?

@ygilliot
Copy link

ygilliot commented Mar 3, 2017

nope...

Narretz pushed a commit to Narretz/angular.js that referenced this issue Mar 6, 2017
@Narretz Narretz modified the milestones: Backlog, 1.6.4 Mar 8, 2017
@Narretz Narretz modified the milestones: 1.6.4, 1.7.0 Mar 23, 2017
@nmschulte
Copy link

nmschulte commented Apr 18, 2017

"bump"

while (form) form = form.$$parentForm;

FYI: This doesn't actually work -- the root FormController has a "noop form" set as its parent; different looping and/or conditional are required.
(e.g. while (form.$$parentForm.hasOwnProperty('$submitted')) form = form.$$parentForm or such).

Note, Angular doesn't expose a static binding for FormController (or any of the types it claims to in the documentation; NgModelController included), otherwise one could use types to check the $$parentForm for real-ness.

@Larvalis
Copy link

Another workaround I used was a watch:

$scope.$watch('form.$$parentForm.$submitted', function (submitted) {
    if (submitted) {
        $scope.form.$setSubmitted(true);
    }
});

@andremw
Copy link

andremw commented Aug 24, 2017

My team got into a situation where we really needed the change from #15778, so what we did was to create a copy of our installed angular.js (say, angular-mod.js), applied the change from the PR and used npm's postinstall to overwrite the original angular.js file in node_modules (a simple cp angular-mod.js node_modules/angular/angular.js).

Worked like a charm.

Narretz pushed a commit to Narretz/angular.js that referenced this issue Nov 7, 2017
@Narretz Narretz self-assigned this Nov 10, 2017
Narretz pushed a commit to Narretz/angular.js that referenced this issue Dec 11, 2017
…tted.

Closes angular#10071

BREAKING CHANGE:

Forms will now set $submitted on child forms when they are submitted.
For example:
```
<form name="parentform" ng-submit="$ctrl.submit()">
  <ng-form name="childform">
    <input type="text" name="input" ng-model="my.model" />
  </ng-form>
  <input type="submit" />
</form>

Submitting this form will set $submitted on "parentform" and "childform".
Previously, it was only set on "parentform".
Narretz added a commit to Narretz/angular.js that referenced this issue Dec 12, 2017
…tted.

Closes angular#10071

BREAKING CHANGE:

Forms will now set $submitted on child forms when they are submitted.
For example:
```
<form name="parentform" ng-submit="$ctrl.submit()">
  <ng-form name="childform">
    <input type="text" name="input" ng-model="my.model" />
  </ng-form>
  <input type="submit" />
</form>

Submitting this form will set $submitted on "parentform" and "childform".
Previously, it was only set on "parentform".

This change was introduced because mixing form and ngForm does not create
logically separate forms, but rather something like input groups.
Therefore, child forms should inherit the submission state from their parent form.
Narretz added a commit to Narretz/angular.js that referenced this issue Dec 12, 2017
…tted

Closes angular#10071

BREAKING CHANGE:

Forms will now set $submitted on child forms when they are submitted.
For example:
```
<form name="parentform" ng-submit="$ctrl.submit()">
  <ng-form name="childform">
    <input type="text" name="input" ng-model="my.model" />
  </ng-form>
  <input type="submit" />
</form>

Submitting this form will set $submitted on "parentform" and "childform".
Previously, it was only set on "parentform".

This change was introduced because mixing form and ngForm does not create
logically separate forms, but rather something like input groups.
Therefore, child forms should inherit the submission state from their parent form.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.