8

EDIT:

I've added a JsFiddle so you can easily troubleshoot instead of having to set up the environment yourself. As you can see, validation is done on the Email field even before the blur event on the input element, which was triggered by the $scope.Email being changed. If you comment out the ng-show="!mainForm.validate()" on the <p> element, you'll see that the issue doesn't take place.


I am using the Angular implementation of jQuery Validate, and I am in need of the ability to check if a form is valid without showing the error messages. The standard solution I've seen online is to use jQuery Validate's checkForm() function, like this:

$('#myform').validate().checkForm()

However, the Angular wrapper I'm using doesn't currently implement the checkForm function. I have been trying to modify the source code to bring it in, and I'm afraid I'm in over my head. The code is small and simple enough that I'll paste it here:

(function (angular, $) {
    angular.module('ngValidate', [])

        .directive('ngValidate', function () {
            return {
                require: 'form',
                restrict: 'A',
                scope: {
                    ngValidate: '='
                },
                link: function (scope, element, attrs, form) {
                    var validator = element.validate(scope.ngValidate);

                    form.validate = function (options) {
                        var oldSettings = validator.settings;

                        validator.settings = $.extend(true, {}, validator.settings, options);

                        var valid = validator.form();

                        validator.settings = oldSettings; // Reset to old settings

                        return valid;
                    };

                    form.numberOfInvalids = function () {
                        return validator.numberOfInvalids();
                    };
                    
                    //This is the part I've tried adding in.
                    //It runs, but still shows error messages when executed.
                    //form.checkForm = function() {
                    //  return validator.checkForm();
                    //}
                }
            };
        })

        .provider('$validator', function () {
            $.validator.setDefaults({
                onsubmit: false // to prevent validating twice
            });

            return {
                setDefaults: $.validator.setDefaults,
                addMethod: $.validator.addMethod,
                setDefaultMessages: function (messages) {
                    angular.extend($.validator.messages, messages);
                },
                format: $.validator.format,
                $get: function () {
                    return {};
                }
            };
        });
}(angular, jQuery));

I want to be able to use it to show or hide a message, like this:

<p class="alert alert-danger" ng-show="!mainForm.checkForm()">Please correct any errors above before saving.</p>

The reason I don't just use !mainForm.validate() is because that causes the error messages to be shown on elements before they are "blurred" away from, which is what I'm trying to avoid. Can anyone help me implement the checkForm() function into this angular directive?

Community
  • 1
  • 1
Jacob Stamm
  • 1,660
  • 1
  • 29
  • 53
  • Please stop inviting me to private chats if you're never going to respond to any of them. – Sparky Mar 08 '17 at 21:11
  • I'm sorry, but I did not receive any notifications that they were responded to. Believe me, I would have hopped on that immediately. The chat functionality is inconsistent and feels unfinished. My apologies. – Jacob Stamm Mar 08 '17 at 21:13
  • Let's cut to the chase: Ignoring everything else, and simply looking at [your jsFiddle demo in the first edit](https://jsfiddle.net/stamminator/0z99py3o/), how is it that demo misbehaving and how should it perform instead? – Sparky Mar 08 '17 at 22:16
  • Seems like a case of "The XY Problem": https://meta.stackexchange.com/a/233676/157574 – Sparky Mar 08 '17 at 22:26
  • @Sparky The demo's misbehaving by doing validation while the user is typing, *before* the blur event on the input. Proper behavior is to do nothing until either the blur event occurs or you try to submit the form. My design differs in that I want to show the default in-line error messages *in addition* to my message at the bottom (very long pages in my app, and users are lost without a message right above the submit button). So, any change to the model runs a $digest cycle, which causes the `validate()` function in the `ng-show` of my custom error message to run, causing premature validation. – Jacob Stamm Mar 09 '17 at 13:30
  • I respectfully disagree that it's an XY problem, because jQuery Validate has already solved this problem with the `checkForm()` function. If I can simply access this underlying function from the Angular plugin, I'll make the `ng-show` of my custom error message say `!mainForm.checkForm()` instead of `!mainForm.validate()`. If this can't be done for technical reasons, then I'll concede and stop using this plugin. But until that's demonstrated, this is still the best solution for Angularizing my existing, massive project, which heavily relies on native jQuery Validate. – Jacob Stamm Mar 09 '17 at 13:37
  • [`.checkForm()` is an undocumented feature](https://jqueryvalidation.org/?s=checkform) that is likely not used very much, further evidenced by [10 SO questions out of 6000](http://stackoverflow.com/search?q=%5Bjquery-validate%5D+checkform%28%29+is%3Aquestion). You've already decided that leveraging the `.checkForm()` method is the only way to achieve your goals, however as per your recent comments, this is not really the root problem... hence, an XY problem. – Sparky Mar 09 '17 at 21:40
  • I have not decided that. As stated in both my question and the bounty, the solution only has to meet my two criteria. For several reasons, I *believe* that checkForm() is the best way to accomplish this, but if it isn't, then that's fine. As long as I have a solution. If you have any ideas for a solution, whether or not involves `checkForm()`, I'm game. I'm just trying to create as little work as possible, and maybe even enhance the plugin for other people, as well. – Jacob Stamm Mar 09 '17 at 23:20

6 Answers6

1

You can add checkForm() function to the plugin as following.

    (function (angular, $) {
    angular.module('ngValidate', [])

        .directive('ngValidate', function () {
            return {
                require: 'form',
                restrict: 'A',
                scope: {
                    ngValidate: '='
                },
                link: function (scope, element, attrs, form) {
                    var validator = element.validate(scope.ngValidate);

                    form.validate = function (options) {
                        var oldSettings = validator.settings;

                        validator.settings = $.extend(true, {}, validator.settings, options);

                        var valid = validator.form();

                        validator.settings = oldSettings; // Reset to old settings

                        return valid;
                    };

                    form.checkForm = function (options) {
                        var oldSettings = validator.settings;

                        validator.settings = $.extend(true, {}, validator.settings, options);

                        var valid = validator.checkForm();

                        validator.submitted = {};

                        validator.settings = oldSettings; // Reset to old settings

                        return valid;
                    };

                    form.numberOfInvalids = function () {
                        return validator.numberOfInvalids();
                    };
                }
            };
        })

        .provider('$validator', function () {
            $.validator.setDefaults({
                onsubmit: false // to prevent validating twice
            });

            return {
                setDefaults: $.validator.setDefaults,
                addMethod: $.validator.addMethod,
                setDefaultMessages: function (messages) {
                    angular.extend($.validator.messages, messages);
                },
                format: $.validator.format,
                $get: function () {
                    return {};
                }
            };
        });
}(angular, jQuery));

Please find the updated jsFiddle here https://jsfiddle.net/b2k4p3aw/

Reference: Jquery Validation: Call Valid without displaying errors?

Community
  • 1
  • 1
Tharaka Wijebandara
  • 7,955
  • 1
  • 28
  • 49
  • This is perfect! Thank you very much. Out of curiosity, why is the line that says `validator.submitted = {}` needed? – Jacob Stamm Mar 13 '17 at 13:21
  • 1
    Basically, input element `onkeyup` event has two listeners. When the user presses a key, first it fires up listener set by angular and start angular digest cycle. This will evaluate `ng-show="!mainForm.checkForm()"` and call `checkForm()` function. After that, the listener set by jQuery validator fires up and try to show errors in `submitted` object which added during execution of `checkForm` function. So if we don't need to show errors after calling `checkForm()`, we have to reset `submitted` object. – Tharaka Wijebandara Mar 13 '17 at 14:47
0

you can achieve onblur event with ng-show="mainForm.Email.$invalid && mainForm.Email.$touched" to <p> tag

by default mainForm.Email.$touched is false, on blur it will change to true

for proper validation change the <input> tag type to email

you can add ng-keydown="mainForm.Email.$touched=false" if you don't want to show error message on editing the input tag

I didn't used angular-validate.js plugin

<div ng-app="PageModule" ng-controller="MainController" class="container"><br />
  <form method="post" name="mainForm" ng-submit="OnSubmit(mainForm)" >
    <label>Email: 
      <input type="email" name="Email" ng-keydown="mainForm.Email.$touched=false" ng-model="Email" class="email" />
    </label><br />
    <p class="alert alert-danger" ng-show="mainForm.Email.$invalid && mainForm.Email.$touched">Please correct any errors above before saving.</p>
    <button type="submit">Submit</button>
  </form>
</div>

Updated code : JSFiddle

AngularJs Form Validation

More info on Angular validation


Update 2

checkForm will return whether the form is valid or invalid

// added checForm, also adds valid and invalid to angular
form.checkForm = function (){
    var valid =  validator.form();

    angular.forEach(validator.successList, function(value, key) {
     scope.$parent[formName][value.name].$setValidity(value.name,true);
    });

    angular.forEach(validator.errorMap, function(value, key) {
      scope.$parent[formName][key].$setValidity(key,false);
    });

    return valid
}

to hide default messages adding by jQuery validation plugin add below snippet, to $.validator.setDefaults

app.config(function ($validatorProvider) {
    $validatorProvider.setDefaults({
        errorPlacement: function(error,element) { // to hide default error messages
              return true;
           }
    });
});

here is the modified plugin looks like

(function (angular, $) {
angular.module('ngValidate', [])

    .directive('ngValidate', function () {
        return {
            require: 'form',
            restrict: 'A',
            scope: {
                ngValidate: '='
            },
            link: function (scope, element, attrs, form) {
                var validator = element.validate(scope.ngValidate);
                var formName = validator.currentForm.name;

                form.validate = function (options) {
                    var oldSettings = validator.settings;

                    validator.settings = $.extend(true, {}, validator.settings, options);

                    var valid = validator.form();

                    validator.settings = oldSettings; // Reset to old settings

                    return valid;
                };

                form.numberOfInvalids = function () {                           
                    return validator.numberOfInvalids();
                };

                // added checkForm
                form.checkForm = function (){
                    var valid =  validator.form();

                    angular.forEach(validator.successList, function(value, key) {
                     scope.$parent[formName][value.name].$setValidity(value.name,true);
                    });

                    angular.forEach(validator.errorMap, function(value, key) {
                      scope.$parent[formName][key].$setValidity(key,false);
                    });

                    return valid
                }
            }
        };
    })

    .provider('$validator', function () {
        $.validator.setDefaults({
            onsubmit: false // to prevent validating twice            
        });

        return {
            setDefaults: $.validator.setDefaults,
            addMethod: $.validator.addMethod,
            setDefaultMessages: function (messages) {
                angular.extend($.validator.messages, messages);
            },
            format: $.validator.format,
            $get: function () {
                return {};
            }
        };
    });
  }(angular, jQuery));

controller

app.controller("MainController", function($scope) {
$scope.Email = "";
$scope.url = "";
$scope.isFormInValid = false; // to hide validation messages
$scope.OnSubmit = function(form) {
    // here you can determine
    $scope.isFormInValid = !$scope.mainForm.checkForm(); 

     return false; 
 }
 })

need to have following on every input tag(example for email)

ng-show="isFormInValid && !mainForm.Email.$invalid "

if the form and email both are invalid the validation message shows up.

JSFiddle

Pavan Kumar Jorrigala
  • 3,085
  • 16
  • 27
  • Your solution does not use the Angular Validate plugin from my example. This is a requirement for my use case. – Jacob Stamm Feb 27 '17 at 14:40
  • so you want to know whether the form is valid or not, using checkForm(), which should return true or false – Pavan Kumar Jorrigala Feb 27 '17 at 22:38
  • Well, the solution doesn't *have* to use the `checkForm()` function — it just has to allow me to check if the form is valid without yet showing error messages *using this particular Angular Validate plugin*. I only draw attention to the `checkForm()` function because it's already built in to jQuery Validate, which this Angular plugin is simply a wrapper for. So, gaining access to this function from the Angular wrapper would probably be the easiest solution. Thanks for helping me out :) – Jacob Stamm Feb 28 '17 at 02:28
  • second point you mentioned in Edit 2, do you ever want to show default error message appending by jQuery Validation plugin – Pavan Kumar Jorrigala Mar 01 '17 at 16:23
  • I'm sorry, I don't understand your question – Jacob Stamm Mar 03 '17 at 14:20
  • there are two error messages are showing up , one is P tag , other one is `Please enter a valid email address.` message next to JSFiddle that is appended by jQuery Validation plugin – Pavan Kumar Jorrigala Mar 03 '17 at 15:31
  • I see. Yes, default error messages should still show up, in addition to the alert at the bottom. – Jacob Stamm Mar 03 '17 at 15:38
  • I updated the code, Is this what you are looking for – Pavan Kumar Jorrigala Mar 03 '17 at 20:16
  • This is really close, but unfortunately the default jQuery Validate functionality, which is to show in-line validation messages on the `blur` event of the input, no longer works. It's only validating everything once you try and submit the form. I really appreciate the work you've put into this, but I think the solution has got to be simpler. Because the `checkForm()` function is already built into jQuery Validate, you shouldn't have to rewrite the function from the ground up. Simply gaining access to this existing function from the Angular side is probably the best and easiest way – Jacob Stamm Mar 05 '17 at 20:56
0

If I understand your question correctly, you want to be able to show an error message when the email adress is invalid and you decide you want to show the error message.

You can achieve this by setting the input type to email like this <input type=email>

Angular adds an property to the form $valid so you can check in your controller if the submitted text is valid. So we only have to access this variable in the controller and invert it. (Because we want to show the error when it is not valid)

$scope.onSubmit = function() {
    // Decide here if you want to show the error message or not
    $scope.mainForm.unvalidSubmit = !$scope.mainForm.$valid
}

I also added a submit button that uses browser validation on submit. This way the onSubmit function won't even get called and the browser will show an error. These methods don't require anything except angularjs. You can check the updated JSFiddle here

Make sure to open your console to see when the onSubmit function gets called and what value gets send when you press the button.

digijap
  • 164
  • 9
  • I only included email validation because I needed a simple example. I am actually using more advanced validation, but that's not what I'm having the issue with. Also, the Angular Validate plugin does not use the `$valid` property like built-in Angular validation does. In my case, the solution has to utilize this plugin. – Jacob Stamm Feb 27 '17 at 14:38
  • Right, so you can add [custom validators](https://docs.angularjs.org/guide/forms) in angular. You create a directive that takes the model value and you can validate the value. This is the same way as the email gets validated. You can read the docs if you need an example or [click here](https://plnkr.co/edit/hHvPq59gsJyDux7PufvW?p=preview) If you want I can add another answer where I explain how it works in detail with another example. – digijap Feb 27 '17 at 15:01
  • Thank you, but as I said, that's not what I'm having the issue with. I have the validation rules fully functional. What I need help with is getting access to jQuery Validate's underlying `checkForm()` function from within the Angular Validate plugin – Jacob Stamm Feb 27 '17 at 15:12
0

You can use $touched, which is true as soon as the field is focused then blurred.

 <p class="alert alert-danger" ng-show="mainForm.Email.$touched && !mainForm.validate()">Please correct any errors above before saving.</p>
opp
  • 1,010
  • 10
  • 17
  • That will work if I only have a single input field I'm validating. In my application, there could be dozens on the page. – Jacob Stamm Mar 02 '17 at 20:50
-1

try this code for validation this is the form

    <form name="userForm" ng-submit="submitForm(userForm.$valid)" novalidate>
    <div class="form-group">   
    <input  type="text"  ng-class="{ 'has-error' : userForm.name.$invalid &&    !userForm.name.$pristine }" ng-model="name" name="name" class="form-control" placeholder="{{ 'regName' | translate }}" required>
  <p ng-show="userForm.name.$invalid && !userForm.name.$pristine" class="help-block">Your name is required.</p>

  </div>

<div class="form-group">
  <input  type="tel" ng-class="{ 'has-error' : userForm.mob.$invalid && !userForm.mob.$pristine }" ng-model="mob" class="form-control" name="mob" ng-maxlength="11" ng-minlength="11"  ng-pattern="/^\d+$/"  placeholder="{{ 'regPhone' | translate }}" required>
       <p ng-show="userForm.mob.$invalid && !userForm.mob.$pristine" class="help-block">Enter a valid number</p>

  </div>
  <div class="form-group">
  <input  type="email"  ng-model="email" name="email" class="form-control" placeholder="{{ 'regEmail' | translate }}"   required>

<p ng-show="userForm.email.$invalid && !userForm.email.$pristine" class="help-block">Enter a valid email.</p>
</div>

<div class="form-group">
  <input   type="password" ng-model="pass" name="pass"  class="form-control" placeholder="{{ 'regPass' | translate }}" minlength="6" maxlength="16" required>
  <p ng-show="userForm.pass.$invalid && !userForm.pass.$pristine" class="help-block"> Too short Min:6 Max:16</p>


  <input  type="password" ng-model="repass"  class="form-control" ng-minlength="6" placeholder="{{ 'regConPass' | translate }}" ng-maxlength="16" required>
  </div>
 <button class="loginbtntwo" type="submit" id="regbtn2"  ng-disabled="userForm.$dirty && userForm.$invalid" translate="signUp" ></button>
  </form>
Assem Mahrous
  • 411
  • 3
  • 15
-1

You will need to modify the Angular Validate Plugin a bit. Here is a working version of your code in JSFiddle. Note the updated plugin code as well as a pair of modifications to your original code.

Updated plugin code simply adds this to validator.SetDefaults parameter:

errorPlacement: function(error,element) { return true; } // to hide default error message

Then we use a scope variable to hide/show the custom error message:

$scope.OnSubmit = function(form) {
if (form.$dirty) {
    if (form.validate()) {
    //form submittal code
    } else {
    $scope.FormInvalid = true;
  }
}
Bane
  • 1
  • 1
  • This is not what I want to do. I want to show the default error messages *at the proper time*, which is only either after the `blur` event on a validated `input` or when I manually execute the `form.validate()` function. I *also* want to show this additional message at the bottom. Simply omitting the default messages is not a solution, I'm afraid. – Jacob Stamm Mar 02 '17 at 20:49