How to validate a form when the submit button is outside of this form, with angularJs?
Asked Answered
C

4

10

I have a form element created with AngularJS with a submit button within it and I would like to put this button outside of this form. How can I do that with Angular and have my form still validated?

e.g main html code:

<div class="sd-chk-steps" ng-show="!step_03" ng-hide="step_03">

<!-- Panel Address -->
<div id="sd-p-chk-1" class="large-8 columns sd-p-chk">
  <div class="cover step_complete" ng-show="step_01" ng-hide="!step_01">
  </div>
  <sd-panel-address >
  <!-- first form -->
  </sd-panel-address>
</div>
<!-- /. Panel Address -->

<!-- Panel delivery -->
<div id="sd-p-chk-2" class="large-8 columns sd-p-chk">
  <div class="cover" ng-show="!step_01" ng-hide="step_01"></div>
  <sd-panel-delivery>
  <!-- second form -->
  </sd-panel-delivery>
  <div class="cover step_complete" ng-show="step_02" ng-hide="!step_02"></div>
</div>
<!-- /. Panel delivery  -->

<!-- Panel payment -->
<div id="sd-p-chk-3" class="large-8 columns sd-p-chk">
  <div class="cover" ng-show="!step_02" ng-hide="step_02"></div>
  <sd-panel-payment>
  <!-- third form -->
  </sd-panel-payment>
</div>
<!-- /. Panel payment -->

<!-- group botton submit -->
<div class="sd-chk-box">
    <div class="sd-chk-box-inner">

        <div class="large-8 columns sd-box-chk-btn sd-chk-btn-sm">
          <button ng-click="clickBtn(shipping.$valid)" type="submit" class="sd-chk-btn sd-button-cta" ng-disabled="shipping.$invalid">
            Next
          </button>
        </div>

        <div class="large-8 columns sd-box-chk-btn sd-chk-btn-sm">
          <button class="sd-chk-btn sd-button-cta" ng-click="clickBtnStep02(formDelivery.$valid)" ng-disabled="formDelivery.$invalid">
            NEXT
          </button>
        </div>

        <div class="large-8 columns sd-box-chk-btn sd-chk-btn-sm">
          <button class="sd-chk-btn sd-button-cta" ng-click="clickBtnStep03(payment.$valid)" ng-disabled="payment.$invalid">
            PLACE ORDER
          </button>
        </div>

    </div>
</div>
<!-- /. group botton submit -->

e.g form html code:

<form id="shipping" class="shipping" name="shipping" 
      novalidate
      ng-submit="form.submit(shipping.$valid)"
      ng-class="{'loading': form.submitting, 'is-el-dirty' : shipping.$dirty || shipping.dirty}">
      <fieldset>
        <div class="row">
          <div class="large-12 columns">
            <label>
              <input type="text" name="name" placeholder="Name" ng-model="form.data.name" required/>
            </label>
            <div class="error" ng-if="shipping.$submitted || shipping.name.$touched" ng-messages="shipping.name.$error">
                <p class="text-msg" ng-message="required">You did not enter your name</p>
            </div>
          </div>
          <div class="large-12 columns">
            <label>
              <input type="text" name="surname" placeholder="Surname" ng-model="form.data.surname" required/>
            </label>
            <div class="error" ng-if="shipping.$submitted || shipping.surname.$touched" ng-messages="shipping.surname.$error">
                <p class="text-msg" ng-message="required">You did not enter your Surname</p>
            </div>
          </div>
        </div>
      </fieldset>
</form>

e.g controller code:

(function() {

  'use strict';

  /**
   * @ngdoc function
   * @name myApp.controller:globalCtrl
   * @description
   * # globalCtrl
   * Controller of the myApp
   */

  var myApp = angular.module('myApp');


  myApp.controller('globalCtrl', function($scope, $locale, $rootScope) {

      // Check if checkbox it's checked
      $scope.checked = true;

      // got to step 2
      $scope.clickBtn = function(form) {
          //valid form
          if (form === true) {
              console.log('Form is valid, $rootScope.step_01= ' + $rootScope.step_01);
              $rootScope.step_01 = true;
              $rootScope.notValidStpe_01 = true;
          }

          //invalid form
          if (form === false) {
              $rootScope.step_01 = false;
              $rootScope.notValidStpe_01 = false;
              console.log('Form is invalid, $rootScope.step_01= ' + $rootScope.step_01);
          }
      };

      // got to step 3
      $scope.clickBtnStep02 = function(form) {
          //valid form
          if (form === true) {
              console.log('Form is valid, $rootScope.step_02 "'+ $rootScope.step_02);
              $rootScope.step_02 = true;
              $rootScope.notValidStpe_02 = true;

          }

          //invalid form
          if (form === false) {
              $rootScope.step_02 = false;
              $rootScope.notValidStpe_02 = false;
              console.log('Form is invalid, $rootScope.step_02= ' + $rootScope.step_02);
          }
      };


      // All steps completed
      $scope.clickBtnStep03 = function(form) {
          //valid form
          if (form === true) {
              console.log('Form is valid, $rootScope.step_03 "'+ $rootScope.step_03);
              $rootScope.step_03 = true;
              $rootScope.notValidStpe_03 = true;

          }

          //invalid form
          if (form === false) {
              $rootScope.step_03 = false;
              $rootScope.notValidStpe_03 = false;
              console.log('Form is invalid, $rootScope.step_03= ' + $rootScope.step_03);
          }
      };

  });

}(window, window.angular));enter code here

e.g app.js code

(function() {

    'use strict';


    var myApp = angular
        .module('myApp', [
            'ngResource',
            'ngAnimate',
            'ngMessages',
            'templates'
        ]);


    myApp.run(function($rootScope) {
        $rootScope.step_01 = false;
        $rootScope.step_02 = false;
        $rootScope.step_03 = false;

        $rootScope.notValidStpe_01 = false;
        $rootScope.notValidStpe_02 = false;
        $rootScope.notValidStpe_03 = false;
    });

}(window, window.angular));
Chattel answered 6/1, 2015 at 14:33 Comment(4)
Isn't angular built upon jQuery so you could just attach an event listener to submit the form upon an element click using .send() ? so like: <form id="form"><input/></form> <button id="button"></button> <script> $("#button").click(function() { $("#form").submit(); }); </script>Furor
What I want to do is to keep the submit button disabled if the form fields are not completed and when you click the button to enable the following form. But this does not work to me if I have the buttons outside the formChattel
It should be completely possible with the ng-directives given? <button ng-disabled="checkFormValidity();" ></button> or something similar?Furor
@Jackhardcastle angular is compatible with jQuery, but definately not built upon it. Angular follows a radically different design principe than jQuery.Discriminator
R
3

As I understand You try to do form wizzard. However you don't need multiple form element, Just use one form element at the top. For child form use ng-form directive to valiadate them seperately.

You can find detailed documentation https://docs.angularjs.org/api/ng/directive/ngForm about using ng-form

Something like this

 <form id="complateOrder" name="orderForm" ng-init="showShippingForm = true">

    <div ng-form="" name="shipping" ng-show="showShippingForm">
       shippig fields
    <button type="button" ng-disabled="shipping.$invalid" ng-click="showDeliveryForm=true">Next</button>
    </div>
    <div ng-form="" name="delivery" ng-show="showDeliveryForm && shipping.$valid" ng-hide="shipping.$invalid" >
       delivery fields
       <button type="button" ng-disabled="shipping.$invalid  && delivery.$invalid" ng-click="showPaymentForm=true">Next</button>
    </div>
    <div ng-form="" name="payment" ng-show="showPaymentForm && shipping.$valid && delivery.$valid " ng-hide="shipping.$invalid && delivery.$invalid">
       payment fields
       <button type="submit" ng-disabled="orderForm.$invalid && shipping.$invalid && payment.$invalid && delivery.$invalid">Submit All</button>
    </div>
    <div>
        <button type="button" ng-click="showPaymentForm ? (showPaymentForm = false; showDeliveryForm= true):showDeliveryForm ? (showDeliveryForm=false; showShippingForm = true):showShippingForm = true">Prev</button>
    </div>

 </form>
Ribble answered 6/1, 2015 at 14:52 Comment(3)
What I'm doing a form of payment. What I want to do is disable the submit button if the form is invalid and when you click the submit button when all fields of the first form is valid remove this item from the DOM: '<div class="cover" ng-show="!step_01" ng-hide="step_01"></div>' and remove the disabled attribute of the fields. The ng-form directive does not work in IE8 and unfortunately to me have to give soperte to IE8Chattel
If you want to support IE8 don't use angular.js Or you must use an older and older version. Just jquery stop support IE8 too for a long time. I advise that, don't think about support IE8. Think about mobility instead of IE8. Because It died...Ribble
I completely agree with you Mehmet Orkun but this does not depend on me. Where I work still give supported to IE 8. For my personal work never give support IE8Chattel
M
24

The clue is using the ngForm directive and assign a form name attribute which references to the $rootScope.

<form name="$root.myForm" id="form_id">
    <input type="text" name="myField" required>
</form>

<button form="form_id" ng-disabled="$root.myForm.myfield.$invalid">
    Submit
</button>

That works.

Monikamoniker answered 3/3, 2015 at 10:26 Comment(2)
you don't need to add it to $rootScope - you could just create a holder object in your current controller, eg, current = {} and then use <form name="current.form" ... >Cancel
Just a note that the form attribute doesn't work in any IE. caniuse.com/#feat=form-attributeDani
R
3

As I understand You try to do form wizzard. However you don't need multiple form element, Just use one form element at the top. For child form use ng-form directive to valiadate them seperately.

You can find detailed documentation https://docs.angularjs.org/api/ng/directive/ngForm about using ng-form

Something like this

 <form id="complateOrder" name="orderForm" ng-init="showShippingForm = true">

    <div ng-form="" name="shipping" ng-show="showShippingForm">
       shippig fields
    <button type="button" ng-disabled="shipping.$invalid" ng-click="showDeliveryForm=true">Next</button>
    </div>
    <div ng-form="" name="delivery" ng-show="showDeliveryForm && shipping.$valid" ng-hide="shipping.$invalid" >
       delivery fields
       <button type="button" ng-disabled="shipping.$invalid  && delivery.$invalid" ng-click="showPaymentForm=true">Next</button>
    </div>
    <div ng-form="" name="payment" ng-show="showPaymentForm && shipping.$valid && delivery.$valid " ng-hide="shipping.$invalid && delivery.$invalid">
       payment fields
       <button type="submit" ng-disabled="orderForm.$invalid && shipping.$invalid && payment.$invalid && delivery.$invalid">Submit All</button>
    </div>
    <div>
        <button type="button" ng-click="showPaymentForm ? (showPaymentForm = false; showDeliveryForm= true):showDeliveryForm ? (showDeliveryForm=false; showShippingForm = true):showShippingForm = true">Prev</button>
    </div>

 </form>
Ribble answered 6/1, 2015 at 14:52 Comment(3)
What I'm doing a form of payment. What I want to do is disable the submit button if the form is invalid and when you click the submit button when all fields of the first form is valid remove this item from the DOM: '<div class="cover" ng-show="!step_01" ng-hide="step_01"></div>' and remove the disabled attribute of the fields. The ng-form directive does not work in IE8 and unfortunately to me have to give soperte to IE8Chattel
If you want to support IE8 don't use angular.js Or you must use an older and older version. Just jquery stop support IE8 too for a long time. I advise that, don't think about support IE8. Think about mobility instead of IE8. Because It died...Ribble
I completely agree with you Mehmet Orkun but this does not depend on me. Where I work still give supported to IE 8. For my personal work never give support IE8Chattel
P
0

If the submit button is outside of the form, then you can wrap your button and form with an ngForm (yes, ngForms can be nested). Give your ngForm a name so that you can handle the submit click and reference the form object by name in the same scope context

Passerine answered 6/1, 2015 at 15:9 Comment(0)
O
0

Even more concise than this answer:

<form name="myForm">
    <input type="text" name="myField" required>
</form>

<button ng-disabled="myForm.myField.$invalid">
    Submit
</button>

(Note that you don't even need to define myForm in the underlying controller and you can also omit the form attribute.)

Overabound answered 29/10, 2015 at 18:19 Comment(1)
This solution does work but with a small problem, the ng-message for $erroron different elements are never called, i.e., error messages associated with the form elements, if any, are not called.Lunch

© 2022 - 2024 — McMap. All rights reserved.