156

I'm writing a simple login form using angularjs with some client side input validation to check that the user name and password is not empty and longer than three characters. See the below code:

<form name="loginform" novalidate ng-submit="login.submit()" class="css-form">
    <fieldset>

        <div class="control-group input-prepend">
            <span class="add-on"><i class="icon-user"></i></span>
            <input type="text" ng-model="login.username" name="username" required ng-minlength="3" placeholder="username" />
        </div>

        <div class="control-group input-prepend">
            <span class="add-on"><i class="icon-lock"></i></span>
            <input type="password" ng-model="login.password" name="password" required ng-minlength="3" placeholder="" />
        </div>

        <div class="control-group">
            <input class="btn" type="submit" value="Log in">
        </div>

    </fieldset>
</form>

And the controller:

var controller = function($scope) {

    $scope.login = {
        submit: function() {

            Console.info($scope.login.username + ' ' + $scope.login.password);
        }
    }

};

The problem is that the login.submit function will be called even if the input is not valid. Is it possible to prevent this behaviour?

As a side note I can mention that I use bootstrap and requirejs as well.

Jim Aho
  • 9,932
  • 15
  • 56
  • 87
Runar Halse
  • 3,528
  • 10
  • 39
  • 59

9 Answers9

331

You can do:

<form name="loginform" novalidate ng-submit="loginform.$valid && login.submit()">

No need for controller checks.

cyberwombat
  • 38,105
  • 35
  • 175
  • 251
  • 8
    This should be the accepted answer. No need to check anything in the controller – Howie Feb 12 '14 at 11:22
  • 10
    I disagree with "no need for controller checks". What if we ant additional validation checks happen in the submit function. – SlaterCodes Jul 31 '14 at 18:42
  • 6
    Perform the additional validation in the directive. Update the validation with $setValidity. – TMB Oct 12 '14 at 22:55
  • @b1tsh1ft It would be preferable to have the current state of the validation be always known rather than figured out on submit. That's kinda the whole point of angular's validation. You can't have any visual indication of an invalid state when the scope doesn't know about it. – Kevin Beal Mar 25 '15 at 18:37
  • @KevinBeal I agree in theory but sometimes need the additional check. My particular case at the time of that comment was on "submit" ran a bunch of custom behavior checks to see if the actions of the user indicated a "bot" (mouse movements, key typing speed, etc.) and would then pop-up additional captcha or flat-out "break" the form at a certain threshold. Best way I found at the time was to run the checks on the actual submit, probably could have been improved on. – SlaterCodes Apr 20 '15 at 15:56
  • 1
    I have tried this approach, but it seems that it evaluates both (i.e. it does not short circuit the boolean after the `&&` I have tried passing in the value as part of the submit and confirmed that the value itself is "false" – Archimedes Trajano Mar 07 '16 at 01:52
  • combining the answer with this is Gorgeous. http://jsfiddle.net/thomporter/ANxmv/2/ – Usman Iqbal Feb 09 '17 at 07:23
  • why we are checking if form is submitted or not with `login.submit()` on `ng-submit` only as i think if we call `ng-submit` function form is submitted obviously. – Gaurav Aggarwal May 16 '17 at 07:47
67

Change the submit button to:

<button type="submit" ng-disabled="loginform.$invalid">Login</button>
Bijan
  • 25,559
  • 8
  • 79
  • 71
43

So the suggested answer from TheHippo did not work for me, instead I ended up sending the form as a parameter to the function like so:

<form name="loginform" novalidate ng-submit="login.submit(loginForm)" class="css-form">

This makes the form available in the controller method:

$scope.login = {
    submit : function(form) {
        if(form.$valid)....
    }
Runar Halse
  • 3,528
  • 10
  • 39
  • 59
  • I had the same issue. Using your method worked, though. – Panda4Man Dec 08 '14 at 16:59
  • This is slightly better than the Accepted Answer because form.$valid check is done in JS and not in HTML. – cellepo May 26 '17 at 17:53
  • However, I don't think you need to pass loginForm to submit function: I think $scope.loginform should be available in the JS (at least it was for me), so you could replace form.$valid with $scope.loginform.$valid. – cellepo May 26 '17 at 17:55
13

HTML:

<div class="control-group">
    <input class="btn" type="submit" value="Log in" ng-click="login.onSubmit($event)">
</div>

In your controller:

$scope.login = {
    onSubmit: function(event) {
        if (dataIsntValid) {
            displayErrors();
            event.preventDefault();
        }
        else {
            submitData();
        }
    }
}
atorscho
  • 2,069
  • 2
  • 15
  • 20
TheHippo
  • 61,720
  • 15
  • 75
  • 100
  • is it really necessary to do the check within the onsubmit method? This will mean you have to check that input meets all criteria both in the markup(via angular) and in the js code. In my opinion angular should not call the onsubmit when the input is invalid. see http://www.benlesh.com/2012/11/angular-js-form-validation.html saying "ng-submit cannot be called until the entire form is $valid" – Runar Halse Apr 28 '13 at 14:59
  • You could also disable the button to not allow submission if form is valid. You could also use the form an ng-submit. If angular does not handle form validation then `event.preventDefault()` is the way to go, to stop form submission. – TheHippo Apr 28 '13 at 18:03
  • @RunarHalse It is really necessary to do the check. I agree with you that it should not be this way. The reason that the onsubmit method is not called in the example that you linked is because that example does not have novalidate set. The browser is preventing the events from raising, not Angular. Here is his example w/ nobalidate on: http://plnkr.co/edit/R0C8XA?p=preview. Notice the form submits. – davidmdem Aug 13 '13 at 21:32
  • perfect just what i was looking for, a way to block form submit using angular – setebos Nov 14 '13 at 11:20
13

Your forms are automatically put into $scope as an object. It can be accessed via $scope[formName]

Below is an example that will work with your original setup and without having to pass the form itself as a parameter in ng-submit.

var controller = function($scope) {

    $scope.login = {
        submit: function() {
            if($scope.loginform.$invalid) return false;

        }
    }

};

Working example: http://plnkr.co/edit/BEWnrP?p=preview

davidmdem
  • 3,763
  • 2
  • 30
  • 33
  • 4
    How would you access the form if you use "As Controller" notation instead of $scope? – masimplo Dec 18 '13 at 12:08
  • @masimakopoulos: You can simply use `if(this.loginform.$invalid)`.. instead but then in the HTML you have to give the form the name `
    – Bart Oct 14 '15 at 13:32
4

Although not a direct solution for the OPs question, if your form is within an ng-app context, but you want Angular to ignore it altogether, you can do this explicitly using the ngNonBindable directive:

<form ng-non-bindable>
  ...
</form>
Ian Clark
  • 9,237
  • 4
  • 32
  • 49
2

Just to add to the answers above,

I was having a 2 regular buttons as shown below. (No type="submit"anywhere)

<button ng-click="clearAll();" class="btn btn-default">Clear Form</button>
<button ng-disabled="form.$invalid" ng-click="submit();"class="btn btn-primary pull-right">Submit</button>

No matter how much i tried, pressing enter once the form was valid, the "Clear Form" button was called, clearing the entire form.

As a workaround,

I had to add a dummy submit button which was disabled and hidden. And This dummy button had to be on top of all the other buttons as shown below.

<button type="submit" ng-hide="true" ng-disabled="true">Dummy</button>

<button ng-click="clearAll();" class="btn btn-default">Clear Form</button>

<button ng-disabled="form.$invalid" ng-click="submit();"class="btn btn-primary pull-right">Submit</button>

Well, my intention was never to submit on Enter, so the above given hack just works fine.

Vighnesh
  • 87
  • 1
  • 8
  • you can stop submit on press enter also by removing the on ng-submit attribute from your form – Kieran Sep 20 '17 at 01:05
1

I know this is an old thread but I thought I'd also make a contribution. My solution being similar to the post already marked as an answer. Some inline JavaScript checks does the trick.

ng-click="form.$invalid ? alert('Please correct the form') : saveTask(task)"
bluish
  • 26,356
  • 27
  • 122
  • 180
Dave Russell
  • 141
  • 1
  • 8
1

I know it's late and was answered, but I'd like to share the neat stuff I made. I created an ng-validate directive that hooks the onsubmit of the form, then it issues prevent-default if the $eval is false:

app.directive('ngValidate', function() {
  return function(scope, element, attrs) {
    if (!element.is('form'))
        throw new Error("ng-validate must be set on a form elment!");

    element.bind("submit", function(event) {
        if (!scope.$eval(attrs.ngValidate, {'$event': event}))
            event.preventDefault();
        if (!scope.$$phase)
            scope.$digest();            
    });
  };
});

In your html:

<form name="offering" method="post" action="offer" ng-validate="<boolean expression">
Ran Cohen
  • 486
  • 1
  • 3
  • 10
  • Thanks for sharing, needed to do a normal form post but validate with angularjs first, ng_validate="formName.$valid" worked – agrath Mar 22 '21 at 22:43