how to set all inputs as $touched without submit button AngularJS

11,363

Solution 1

You could follow a format similar to this:

if ($scope.form.$invalid) {
    angular.forEach($scope.form.$error, function (field) {
        angular.forEach(field, function(errorField){
            errorField.$setTouched();
        });
    });
}

See '$setTouched': https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

Solution 2

For nested forms:

function setFormTouched(form) {
        // Check if the form/property has the $setSubmitted method
        if (form.hasOwnProperty('$submitted')) {
            // Iterate through each of the required error properties
            angular.forEach(form.$error, function (errorType) {
                // Iterate through each error type
                angular.forEach(errorType, function (prop) {
                    // Check if the property has the $setTouched method
                    if (prop.hasOwnProperty('$touched')) prop.$setTouched();
                    // Recursive call to handle nested forms
                    setFormTouched(prop);
                });

            });
        }
    }

Solution is based on codepen by Patrick Marabeas But I had to edit the original code because hasOwnProperty didn't work for functions defined in prototype. For example:

if(prop.hasOwnProperty('$setTouched'))

Always returned false.

EDIT: What I originally needed was a way to make it easier for the user to find errors in nested tabs. This was the final working scope function used for disabling/enabling submit button and error message:

$scope.isFormValid = function (topLevelForm) {
        if (topLevelForm.$valid) {
            return true;
        }

        // Form not valid, triggering fields to make it easier to find errors
        setFormTouched(topLevelForm);

        function setFormTouched(form) {
            // Check if the form/property has the $setSubmitted method
            if (form.hasOwnProperty('$submitted')) {
                // Iterate through each of the required error properties
                angular.forEach(form.$error, function (errorType) {
                    // Iterate through each error type
                    angular.forEach(errorType, function (prop) {
                        // Check if the property has the $setTouched method
                        if (prop.hasOwnProperty('$touched')) prop.$setTouched();
                        // Recursive call to handle nested forms
                        setFormTouched(prop);
                    });

                });
            }
        }
        return false;
    };
Share:
11,363
yl2015
Author by

yl2015

Updated on June 14, 2022

Comments

  • yl2015
    yl2015 almost 2 years

    I have a directive that validates inputs inside a form based on their $valid and $untouched properties - if the input was "touched" it checks for validation and colors the font and border in red/green accordingly, if the input wasn't "touched" it won't do anything.

    I'm using ngBootBox's custom dialog so i don't have the type="submit" kind of button for submitting the form, I'm using the callback function of the "Create" button in order to pass/save the data.

    My problem is that when I click the "create" button and the form is not "valid" because some of the fields are empty - my inputs are still "untouched" so the $watch function isn't being called. Any solutions? Is there a way to do something like this: $scope.createProjectForm.$setTouched(true); that will make all child inputs of that form to get that value?

    I also tried that and it didn't work:

    angular.forEach($scope.createProjectForm, function(field){
       field.$setTouched(true);
    });
    

    this is my validation directive:

    angular.module('mean.theme').directive("inputValidation", function () {
        return {
            restrict: 'EA',
            require: 'ngModel',
            link: function (scope, inputElement, attrs, ngModelCtrl) {
                var $icon = $('<i class="fa"></i>');
                inputElement.before($icon);
                scope.$watchGroup([
                    function(){
                        return ngModelCtrl.$untouched;
                    },
                    function(){
                        return ngModelCtrl.$valid;
                    }
                ], function(Paramaters){
                    console.log(scope);
    
                    if(!Paramaters[0]) {
                        if (Paramaters[1]) {
                            inputElement.switchClass('validation-warning-border','validation-success-border',50)
                            inputElement.prev().switchClass('fa-warning validation-warning-font' , 'fa-check validation-success-font',50);
                        } else {
                            inputElement.switchClass('validation-success-border','validation-warning-border',50)
                            inputElement.prev().switchClass('fa-check validation-success-font','fa-warning validation-warning-font',50)
                        }
                    }
                });
            }
        };
    });
    

    and this is part of my controller code:

        $scope.create = function () {
            var options = {
                title: 'Create',
                templateUrl: 'project.html',
                scope: $scope,
                buttons: {
                    warning: {
                        label: "Cancel",
                        className: "btn-link",
                        callback: function () {
                        }
                    },
                    success: {
                        label: "Create",
                        className: "green",
                        callback: function () {
                            if ($scope.createProjectForm.$valid){
                                $scope.createProject(template);
                            } else {
                                $scope.project.createButtonClicked = true;
                                return false;
                            }
                        }
                    }
                }
            };
            $ngBootbox.customDialog(options);
        };
    

    and this is part of my HTML code:

    <form role="form" name="createProjectForm">
        <label>
            Name Your Project
        </label>
        <div>
            <input type="text" name="project.name"
                   ng-model="project.name" required="required" class="form control" input-validation/>
        </div>
        <label>
            Name Your Project
        </label>
        <div>
            <input type="text" name="project.title"
                   ng-model="project.title" required="required" class="form control" input-validation/>
        </div>
    </form>
    
    • EDIT:

    I found what i needed, easier and shorter way to do it:

    It is possible to set manually: $scope.createProjectForm.$setSubmitted() to true

    and then make the children(inputs) $watch for this change as well:

    scope.$watchGroup([
                function(){
                    return ngModelCtrl.$untouched;
                },
                function(){
                    return ngModelCtrl.$valid;
                },
                function(){
                    return ngModelCtrl.$$parentForm.$submitted;
                }
            ], function(Paramaters){
             // code here
            }