Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent disabled form fields from being validated

Tags:

angularjs

Update

I have created jsfiddles to illustrate my problem since the question was not clear.

First I thought that I can't deactivate validation of form fields but I learned that this can be done with the ng-required directive.

http://jsfiddle.net/charms/YhVfN/23/

My actual problem is that the password fields are not set to the pristine state. All other form fields are cleared but the password fields are not cleared.

To test this behaviour you can:

  • Click on to the "Add user" button
  • Add an email address, firstname, lastname, password and a wrong password for the confirmation
  • Click on to the close button without saving
  • Click on to the edit button

And then you'll see that the password is still set. It is not cleared by the $setPristine method. All other form fields are cleared.

http://jsfiddle.net/charms/eP7T8/61/

Previous (outdated question)

Does anybody know how I can deactivate validation for disabled fields? I'm using angularjs 1.1.3.

When I'm creating a new user I would like to validate all fields, inclusive password fields. But when I edit a user I would like that the password fields stay inactive and are not validated, unless the user activates the fields over a checkbox.

My current problem is that if I set the password fields to ng-disabled=true the validation is still kicking in. I thought that ng-required=false might help but it doesn't.

I'm setting the password field to ng-disabled=true and ng-required=false.

<input type="password" name="password" ng-model="password" placeholder="Password" ng-disabled="true" ng-required="false" required/>

I'm setting the password confirmation field to ng-disabled=true and ng-required=false.

<input type="password" name="passwordConfirm" ng-model="passwordConfirm" placeholder="Confirm Password" ng-disabled="true" ng-required="false" required field-equal="password"/>

But still the required and field-equal directives are being validated. How can I prevent validation on those deactivated fields?

Below my full code.

HTML:

<div id="user_list" class="listview_list">
<div id="user_row" class="listview_row" ng-repeat="u in users">
    <div id="user_username" class="listview_column"><span class="listview_mainfield">{{u.email}}</span></div>
    <div id="user_firstname" class="listview_column">{{u.firstName}}</div>
    <div id="user_lastname" class="listview_column">{{u.lastName}}</div>
    <button class="listview_row_button" ng-click="deleteUser(u.id, $index)">Delete</button>
    <button class="listview_row_button" ng-click="showEditScreen(u.id, $index)">Edit</button>
</div>
</div>
<div id="user_new" class="new_user">
<button class="new_user_button" ng-click="showAddScreen()">Add user</button>
</div>

<div id="user_mod" class="user_form" ng-show="userModScreenIsVisible">
<form name="mod_user" novalidate>
    <input type="email" name="email" ng-model="user.email" placeholder="E-Mail" ng-disabled="emailFieldDisabled" required email-available/>
    <div class="user-help" ng-show="mod_user.email.$dirty && mod_user.email.$invalid">Invalid:
        <span ng-show="mod_user.email.$error.required">Please enter your email.</span>
        <span ng-show="mod_user.email.$error.email">This is not a valid email.</span>
        <span ng-show="mod_user.email.$error.checkingEmail">Checking email...</span>
        <span ng-show="mod_user.email.$error.emailAvailable">Email not available</span>
    </div>
    <br/>
    <input name="firstName" ng-model="user.firstName" placeholder="Firstname" required/>
    <div class="user-help" ng-show="mod_user.firstName.$dirty && mod_user.firstName.$invalid">Invalid:
        <span ng-show="mod_user.firstName.$error.required">Please enter your firstname.</span>
    </div>
    <br/>
    <input name="lastName" ng-model="user.lastName" placeholder="Lastname" required/>
    <div class="user-help" ng-show="mod_user.lastName.$dirty && mod_user.lastName.$invalid">Invalid:
        <span ng-show="mod_user.lastName.$error.required">Please enter your lastname.</span>
    </div>
    <br/>
    <input type="checkbox" name="setPassword" ng-disabled="passwordCheckboxDisabled" ng-show="passwordCheckboxVisible"/>
    <span class="password_checkbox" ng-show="passwordCheckboxVisible">Change password</span>
    <br ng-show="passwordCheckboxVisible"/>
    <input type="password" name="password" ng-model="password" placeholder="Password" ng-disabled="passwordFieldDisabled" ng-required="passwordFieldRequired" required/>
    <div class="user-help" ng-show="mod_user.password.$dirty && mod_user.password.$invalid">Invalid:
        <span ng-show="mod_user.password.$error.required">Please enter a password.</span>
    </div>
    <br/>
    <input type="password" name="passwordConfirm" ng-model="passwordConfirm" placeholder="Confirm Password" ng-disabled="passwordFieldDisabled" ng-required="passwordFieldRequired" required field-equal="password"/>
    <div class="user-help" ng-show="mod_user.passwordConfirm.$dirty && mod_user.passwordConfirm.$invalid">Invalid:
        <span ng-show="mod_user.passwordConfirm.$error.required">Please enter a password.</span>
        <span ng-show="mod_user.passwordConfirm.$error.fieldEqual">Passwords do not match.</span>
    </div>
    <br/>
    <button class="button" ng-click="hideUserModScreen()">Close</button>
    <button class="button" ng-click="updateUserDetails()" ng-disabled="mod_user.$invalid" ng-show="updateUserDetailsButtonIsVisible">Update</button>
    <button class="button" ng-click="saveUserDetails()" ng-disabled="mod_user.$invalid" ng-show="saveUserDetailsButtonIsVisible">Save</button>
</form>
</div>

Controller:

'use strict';

/*
* Controller to display and manipulate users.
*/
function UserCtrl($scope, User) {
// initialize as invisible
$scope.userModScreenIsVisible = false;
$scope.updateUserDetailsButtonIsVisible = false;
$scope.saveUserDetailsButtonIsVisible = false;
$scope.passwordCheckboxVisible = false;

// initialize as disabled or enabled
$scope.emailFieldDisabled = false;
$scope.passwordCheckboxDisabled = false;
$scope.passwordFieldDisabled = false;

// initialize required or not required
$scope.passwordFieldRequired = false;

// gather array index before opening edit
// screen (used in updateUserDetails method)
$scope.editIndex = null;

// display list with users
$scope.getList = function() {
    User.query(
        {}, //params
        function (data) { //success
            $scope.users = data.data;
        },
        function (data) { //failure
            console.log("Error occurred while getting list of users");
        });
}

// execute getList() when partial is loaded
$scope.getList();

// show edit screen if edit button is clicked
$scope.showEditScreen = function(id, index) {
    $scope.user = User.get({userId: id});
    $scope.editIndex = index;
    $scope.updateUserDetailsButtonIsVisible = true;
    $scope.userModScreenIsVisible = true;
    $scope.emailFieldDisabled = true;
    $scope.passwordCheckboxVisible = true;
    $scope.passwordFieldDisabled = true;
    $scope.passwordFieldRequired = false;
    $scope.passwordCheckboxDisabled = false;
    //console.log($scope.mod_user);
}

// show add screen if the add button is clicked
$scope.showAddScreen = function() {
    $scope.user = new User();
    $scope.saveUserDetailsButtonIsVisible = true;
    $scope.passwordCheckboxDisabled = true;
    $scope.passwordFieldRequired = true;
    $scope.userModScreenIsVisible = true;
    console.log($scope.mod_user);
}

// hide edit screen if close button is clicked
$scope.hideUserModScreen = function() {
    $scope.updateUserDetailsButtonIsVisible = false;
    $scope.saveUserDetailsButtonIsVisible = false;
    $scope.userModScreenIsVisible = false;
    $scope.emailFieldDisabled = false;
    $scope.passwordFieldDisabled = false;
    $scope.passwordFieldRequired = false;
    $scope.passwordCheckboxVisible = false;
    $scope.passwordConfirm = '';
    $scope.password = '';
    $scope.mod_user.$setPristine();
}

// update a user
$scope.updateUserDetails = function() {
    $scope.user.$update();
    angular.extend($scope.users[$scope.editIndex], $scope.user);
    $scope.editIndex = null;
    $scope.updateUserDetailsButtonIsVisible = false;
    $scope.userModScreenIsVisible = false;
    //console.log($scope.mod_user);
}

// save a new user
$scope.saveUserDetails = function() {
    $scope.user.$create();
    $scope.users.push($scope.user);
    $scope.saveUserDetailsButtonIsVisible = false;
    $scope.userModScreenIsVisible = false;
}

// delete a user
$scope.deleteUser = function(id, index) {
    User.delete({userId: id});
    $scope.users.splice(index, 1);
    $scope.userModScreenIsVisible = false;
}
}

Directives:

'use strict';

/* Directives */


angular.module('myApp.directives', []).
directive('appVersion', ['version', function (version) {
    return function (scope, elm, attrs) {
        elm.text(version);
    };
}]).

/*
 * Validate if the email address is available.
 */
directive('emailAvailable', function($http) { // available
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            // push the validator on so it runs last.
            ctrl.$parsers.push(function(viewValue) {
                // set it to true here, otherwise it will not
                // clear out when previous validators fail.
                ctrl.$setValidity('emailAvailable', true);
                if(ctrl.$valid) {
                    // set it to false here, because if we need to check
                    // the validity of the email, it's invalid until the
                    // AJAX responds.
                    ctrl.$setValidity('checkingEmail', false);

                    // check if email is available or used
                    if(viewValue !== "" && typeof viewValue !== "undefined") {
                        $http.get('/api/user/email/' + viewValue + '/available')
                            .success(function(data, status, headers, config) {
                                ctrl.$setValidity('emailAvailable', true);
                                ctrl.$setValidity('checkingEmail', true);
                            })
                            .error(function(data, status, headers, config) {
                                ctrl.$setValidity('emailAvailable', false);
                                ctrl.$setValidity('checkingEmail', true);
                            });
                    } else {
                        ctrl.$setValidity('emailAvailable', false);
                        ctrl.$setValidity('checkingEmail', true);
                    }
                }
                return viewValue;
            });
        }
    };
}).

/*
 * Validate if two fields are equal (such as passwords match for example
 */
directive('fieldEqual', [function() {
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            ctrl.$parsers.push(function(viewValue) {
                ctrl.$setValidity('fieldEqual', true);
                if(ctrl.$valid) {
                    scope.$watch(attr.fieldEqual, function() {
                        var compareValue = this.last;
                        if (viewValue !== compareValue) {
                            ctrl.$setValidity('fieldEqual', false);
                            return undefined;
                        } else {
                            ctrl.$setValidity('fieldEqual', true);
                            return viewValue;
                        }
                    });
                }
            });
        }
    };
}]);
like image 482
Christopher Armstrong Avatar asked Mar 31 '13 16:03

Christopher Armstrong


1 Answers

It seems that I have confused myself with what $setPristine does.

I have expected that $setPristine will not only set the $pristine state to true, but will also recursively clear my form fields. This doesn't seem to be the case. $setPristine does only set the state of $pristine to true globally and in all the controls.

I have been looking at the advancedForm example on the Angularjs page and have seen that they create a master to pre-fill the fields:

http://docs.angularjs.org/cookbook/advancedform

So as solution I have just created an empty master:

http://jsfiddle.net/charms/AhGDC/24/

Controller

var $scope;
var app = angular.module('myapp', []);

function UserCtrl($scope) {
$scope.showField = true;
$scope.reset = function() {
    var master = { name: '' };
    $scope.temp = angular.copy(master);
    $scope.user_form.$setPristine();
}  
}

HTML

<div ng-app="myapp">
<div ng-controller="UserCtrl">
    <form name="user_form" novalidate>
        <input name="name" ng-model="temp.name" ng-show="showField" placeholder="Name" required/>
        <button class="button" ng-click="reset()">Reset</button>     
    </form>
    <p>
        Pristine: {{user_form.$pristine}}
    </p>
    <p>
    <pre>Errors: {{user_form.$error | json}}</pre>
    </p>
</div>
</div>

So I guess this is the way to go. If somebody has a better solution to reset form fields it would be great to hear about it.

I thought Angularjs might have a more elegant solution. Also looking at the setPristine source did not reveal anything else.

like image 83
Christopher Armstrong Avatar answered Oct 13 '22 01:10

Christopher Armstrong