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;
}
});
}
});
}
};
}]);
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.
Related
I have a form with 2 password inputs.
I use a directive to validate that the 2 forms are identical to each other.
It currently works if you fill in the password1 first and the password2 second.
Problem: When you fill in password1 and password2 and they're equal, and after that you change password1, the error messages don't get updated. I would have to type in password2 again for the error messages to appear.
Template
<!-- Password1-->
<div class="form-group"
ng-class="{ 'has-error' : userForm.password.$touched && userForm.password.$invalid,
'has-success' : userForm.password.$valid }">
<div class="col-10">
<input class="form-control" type="password" placeholder="Password"
name="password"
ng-model="home.user.password"
ng-minlength="8"
ng-maxlength="30"
required>
<div class="help-block" ng-messages="userForm.password.$error" ng-if="userForm.password.$touched">
<p ng-message="minlength">Your password is too short.</p>
<p ng-message="maxlength">Your password is too long.</p>
<p ng-message="required">Your password is required.</p>
</div>
</div>
</div>
<!-- Password2-->
<div class="form-group"
ng-class="{ 'has-error' : userForm.password2.$touched && userForm.password2.$invalid,
'has-success' : userForm.password2.$valid }">
<div class="col-10">
<input class="form-control" type="password" placeholder="Confirm your password"
name="password2"
ng-model="home.user.password2"
ng-minlength="8"
ng-maxlength="30"
password-verify="home.user.password"
required>
<div class="help-block" ng-messages="userForm.password2.$error" ng-if="userForm.password2.$touched">
<p ng-message="passwordVerify">Passwords do not match.</p>
</div>
</div>
</div>
passwordVerify directive
.directive('passwordVerify', passwordVerify);
function passwordVerify() {
var directive = {}
directive.require = "ngModel";
directive.scope = { passwordVerify: '=' };
directive.link = verifyPassword;
return directive;
function verifyPassword(scope, element, attrs, ctrl) {
scope.$watch('passwordVerify', WatcherPassword1, callback);
scope.$watch(WatcherPassword2, callback);
function WatcherPassword1() {
var combined;
console.log(scope.passwordVerify);
if (scope.passwordVerify || ctrl.$viewValue) {
combined = scope.passwordVerify + '_' + ctrl.$viewValue;
}
return combined;
}
function WatcherPassword2() {
var combined;
console.log(ctrl.$viewValue);
if (scope.passwordVerify || ctrl.$viewValue) {
combined = scope.passwordVerify + '_' + ctrl.$viewValue;
}
return combined;
}
function callback(value) {
if (value) {
ctrl.$parsers.unshift(function(viewValue) {
var origin = scope.passwordVerify;
if (origin !== viewValue) {
ctrl.$setValidity("passwordVerify", false);
return undefined;
} else {
ctrl.$setValidity("passwordVerify", true);
return viewValue;
}
});
}
}
}
}
I think you need also $watch first input password model in directive
UPDATE
So I found the problem, $watchers areworking good, it was not working because of
ctrl.$parsers.unshift. ctrl.$parsers.unshift executes only if ctrl was modified by user. Check my JSFiddle example
.directive('passwordVerify', passwordVerify);
function passwordVerify() {
return {
require: "ngModel",
scope: {
passwordVerify: '='
},
link: function(scope, element, attrs, ctrl) {
function checkPasswords(){
console.log(viewValue);
var origin = scope.passwordVerify;
if (origin !== ctrl.$viewValue) {
ctrl.$setValidity("passwordVerify", false);
return undefined;
} else {
ctrl.$setValidity("passwordVerify", true);
return ctrl.$viewValue;
}
});
scope.$watch('passwordVerify', function(){
// first input changed
}, function(){
checkPasswords()
})
scope.$watch(function() {
... code here ...
}, function(){
checkPasswords()
})
I am newbie in angularjs, and I have a question.
I want to write only a directive for validate all types data (string, number,etc).
I wrote in JSFIDDLE,you can visit here: http://jsfiddle.net/wk8rbot5/5/, it worked.
But I dont want to use if-else to much in this file. I think we can use type or regex from other file. So anyone has suggestion, please let me know. Thank you so much.
var app = angular.module("app",[]);
app.controller("FormCtrl", function($scope) {
$scope.data = {};
$scope.action = function() {
debugger
console.log($scope.data);
}
});
app.directive("allowPattern", function() {
return {
restrict: "A",
require: "ngModel",
link: function(scope, el, attrs, ngModel) {
var validator, patternValidator,
validType = attrs.allowPattern,
required = true;
var pattern = "";
if( validType =="email" ) {
pattern = new RegExp(/^[A-Za-z0-9_-]+([\.]?[A-Za-z0-9_-])*#\w+([-.]\w+)*\.\w+([-.]\w+)*$/);
}
else if(validType =="onlynumber"){
//do other validate here
pattern = new RegExp(/^[0-9]*$/);
} else{
parttern = new RegExp(/^.*S/); // !!! SyntaxError is fixed in this line
}
patternValidator = function(value) {
return validate(pattern, value)
};
ngModel.$formatters.push(patternValidator);
ngModel.$parsers.push(patternValidator);
attrs.$observe("required", function(newval) {
required = newval;
patternValidator(ngModel.$viewValue);
});
function validate(regexp, value) {
if( value == null || value === "" || !required || regexp.test(value) ) { // !!! TypeError: regexp.test is not a function
ngModel.$setValidity('pattern', true);
return value;
}
else {
ngModel.$setValidity('pattern', false);
return;
}
}
}
};
});
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="app" ng-controller="FormCtrl">
<form name="theForm" novalidate="novalidate">
<input type="text" ng-model="data.name" allow-pattern="email" required/>
<button ng-disabled="theForm.$invalid" ng-click="action()">Action</button>
</form>
<br>
other form:
<br>
<form name="theForm2" novalidate="novalidate">
<input type="text" ng-model="data.number" allow-pattern="onlynumber" required/>
<button ng-disabled="theForm2.$invalid" ng-click="action2()">Action</button>
</form>
<br>
other form 3 :
<br>
<form name="theForm3" novalidate="novalidate">
<input type="text" ng-model="data.abc" allow-pattern="" required/>
<button ng-disabled="theForm3.$invalid" ng-click="action3()">Action</button>
</form>
</div>
I'm new to web and can't figure this one out:
I have a form that I want the submit button to be disabled while the form is invalid.
I've tried using the X.$valid and the X.checkValidity() but with no help. I've also looked at the iron-form examples and documentation but I can't
I assume my trouble is the use of Angular + Polymer but I can't find a solution to how to get the behavior I want.
here is my code:
<form id="loginForm" novalidate>
<paper-input ng-model="username" label="{{::tr('Enter a username')}}" required auto-validate error-message="{{::tr('Please enter your username')}}" ng-keyup="keyPress($event.keyCode)" ng-change="password = ''"></paper-input>
<paper-input ng-model="password" label="{{::tr('Enter a password')}}" required auto-validate error-message="{{::tr('Please enter your password')}}" type="password" ng-keyup="keyPress($event.keyCode)"></paper-input>
<div id="loginFailureReason"></div>
<div class="pm4-form-footer">
<paper-button raised ng-click="forgotPassword();" ng-enable="!loading">{{::tr('Forgot your password') }}</paper-button>
<paper-button raised type="submit" ng-click="loginForm.$valid && login()" ng-enable="loginForm.$valid && !loading">{{::tr('Sign in')}}</paper-button>
</div>
</form>
Code of the controller:
loginApp.controller('LoginController', ["$scope", "$http", "$window","trFilter",
function ($scope, $http, $window, tr) {
//Used to determine if to present the reset password form or not
$scope.resettingPassword = false;
//Used to determine if to present the reset code form or not
$scope.submitResetCode = false;
//Model fields that will be sent to server
$scope.username = '';
$scope.password = '';
$scope.login = function () {
$scope.loading = true;
$('input[ng-model], select[ng-model]').each(function () {
angular.element(this).controller('ngModel').$setViewValue($(this).val());
});
$http.post('/Account/DoLogin', { username: $scope.username, password: $scope.password }).
success(function (data, status, headers) {
if (headers('AccountCtrResponse') !== null && headers('AccountCtrResponse') === "Done") {
$window.location.replace("/" + $window.location.hash);
} else {
$scope.logonFailureReason = tr("Login failed due to the following reason: " +headers('AccountCtrResponse'));
$scope.loading = false;
}
}).
error(function () {
$scope.logonFailureReason = "Failure in submitting the request. Try again later or report this if it persists.";
$scope.loading = false;
});
};
If I add the following code it works but I think it is incorrect to put that logic in the controller. In the html:
<paper-button id="submitionButton" raised type="submit" ng-disabled="!formValidity()">
{{::tr('Sign in')}}
</paper-button>
In the controller:
$scope.formValidity = function () {
return loginForm.checkValidity();
};
Try:
<form name="loginForm" >
<paper-input ng-model="username" label="{{::tr('Enter a username')}}" required auto-validate error-message="{{::tr('Please enter your username')}}" ng-keyup="keyPress($event.keyCode)" ng-change="password = ''"></paper-input>
<paper-input ng-model="password" label="{{::tr('Enter a password')}}" required auto-validate error-message="{{::tr('Please enter your password')}}" type="password" ng-keyup="keyPress($event.keyCode)"></paper-input>
<div id="loginFailureReason"></div>
<div class="pm4-form-footer">
<paper-button raised ng-click="forgotPassword();" ng-disabled="loading">{{::tr('Forgot your password') }}</paper-button>
<paper-button raised type="submit" ng-click="login()" ng-disabled="!loginForm.checkValidity()">{{::tr('Sign in')}}</paper-button>
</div>
</form>
We have a validation directive that we use to validate the controls on Blur and viewContentLoaded event.
We persist the form values in local storage to remember the details when we navigate away from the form and come back
The problem is that, even though it remembers the details it doesn't re-validate the Firstname and Lastname when we load that view again.
Following is our original code:
<div>
<form name="form" class="form-horizontal">
<label class="control-label">First name</label>
<div class="controls">
<input id="firstName" name="FirstName" ng-model="order.FirstName" type="text" validate="alphabeticOnly" maxLength="30" required/>
<span class="help-block" ng-show="form.FirstName.$dirty && form.FirstName.$invalid">Please enter valid Firstname</span>
</div>
</div>
<div class="control-group">
<label class="control-label">Last name</label>
<div class="controls">
<input id="lastName" name="LastName" ng-model="order.LastName" type="text" validate="alphabeticOnly" maxLength="30" required/>
<span class="help-block" ng-show="form.LastName.$dirty && form.LastName.$invalid">Please enter valid Lastname</span>
</div>
</div>
</form>
Confirm
The next function just saves the order and redirects to another page.
We have a $watch on $scope.order that saves the data in local storage to remember.
Directive:
.directive('validate', ['validationService', function(validationService) {
function validate(elm) {
var fn = elm.attr("validate");
var value = elm.val();
return validationService[fn](value);
}
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
function triggerValidations(){
checkValidity();
ctrl.$parsers.unshift(function(viewValue) {
checkValidity();
return viewValue;
});
ctrl.$formatters.unshift(function(viewValue) {
checkValidity();
return viewValue;
});
}
function checkValidity(){
if (elm.val().length > 0)
{
var isValid = validate(elm);
ctrl.$setValidity('validate', isValid);
console.log(" here i am - CheckValidity", attrs.id, elm.val(), isValid );
//scope.$apply();
}
}
$rootScope.$on('$viewContentLoaded', triggerValidations);
elm.bind('blur', function(event) {
scope.$apply(function() {
ctrl.$setValidity('validate', validate(elm));
});
});
}
};
}])
If we add $scope.apply it gives an error "$digest already in progress"
At the end, we want to validate the form when someone lands onto the page.
I am trying to show a Validation Summary, a Div on top of the page with all the Validation error messages in angularjs, on form submit.
I am using the below logic to show/ hide the top div with validation messages,
<div class="alert alert-error" ng-show="submitted && myForm.$invalid">
</div>
I am setting the variable submitted to true on save button click. It's working Okay the first time, but after the first submission, if enter the value for the input field(required field) and clear it, it's kicking off the validation(the top div shows).
Is there a way to show the validation div, only on the submit of the form and not when the user clears the input field ?
UPDATE
$scope.save = function (myForm) {
$scope.submitted = true;
if (myForm.$invalid) {
return;
}
$scope.submitted = false;
}
Thanks !
Hmm one way to do it would be to watch the FormController's property $dirty and $setPristine() method, and use a variable hasError to show the error or not.
See this plunker as an example
JAVASCRIPT
controller('AppController', ['$scope', function($scope) {
$scope.hasError = false;
$scope.$watch('theForm.$dirty', function() {
$scope.hasError = false;
$scope.theForm.$setPristine();
});
$scope.save = function() {
$scope.hasError = $scope.theForm.$invalid;
if($scope.hasError) {
// perform error routine
} else {
// perform save routine
}
};
}]);
HTML
<body ng-controller="AppController">
<div class="error" ng-show="hasError">This is an error</div>
<form name="theForm" ng-submit="save()" novalidate>
<input type="text" name="text1" ng-model="text1" required>
<input type="text" name="text2" ng-model="text2" required>
<button type="submit">Submit</button>
</form>
</body>
Make sure you are setting submitted to false upon display of your validation div else it'll show up after each additional validation call.
$scope.save = function (myForm) {
$scope.submitted = true;
if (myForm.$invalid) {
$scope.submitted = false;
return;
}
}