I first using setValidity to make a directive in Angular.but not as my expected,here is my code:
angular.module('myApp', [])
.controller('ctrl',function($scope){
$scope.pw='';
})
.directive('pwCheck', function(){
return {
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
elem.on('keyup', function () {
scope.$apply(function () {
var len = elem.val().length;
if(len===0){
ctrl.$setValidity('zero',true);
} else if(len>1 && len<6){
ctrl.$setValidity('one',true);
} else {
ctrl.$setValidity('two',true);
}
});
});
}
};
});
HTML:
<body ng-controller="ctrl">
<form id="myForm" name="myForm">
<input type="text" ng-model="pw" pw-check />
{{myForm.$error}}
<div class="msg-block" ng-show="myForm.$error">
<span class="msg-error" ng-show="myForm.pw.$error.zero">
Input a password.
</span>
<span class="msg-error" ng-show="myForm.pw.$error.one">
Passwords too short.
</span>
<span class="msg-error" ng-show="myForm.pw.$error.two">
Great.
</span>
</div>
</form>
</body>
Online Demo:
http://jsbin.com/cefecicu/1/edit
I think you need:
//Reset your validity
ctrl.$setValidity('zero',true);
ctrl.$setValidity('one',true);
ctrl.$setValidity('two',true);
if(len===0){
ctrl.$setValidity('zero',false);
} else if(len>=1 && len<6){ //use len>=1 instead
ctrl.$setValidity('one',false);
} else {
ctrl.$setValidity('two',false);
}
Using false to indicate errors (not valid):
And give a name to your input:
<input type="text" ng-model="pw" name="pw" pw-check />
http://jsbin.com/cefecicu/11/edit
Related
I'm trying to validate two password input fields. Simply confirm that they are equal. (Suggest another approach if mine is way wrong)
I have implemented a directive with a simple validation that checks if the "confirm" password is the same as the original. But the directive also checks for other things, so I need to have both input fields to have it.
The problem is that when I have my directive on both input fields, I cannot read their model values through the attribute (to check if they match).
Here is a working demo without the directive on the first password:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
});
app.directive('myDir', function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
ctrl.$validators.mismatch = function(modelValue, viewValue) {
// MAIN CODE:
return viewValue === scope.$eval(attrs.confirm);
};
ctrl.$validators.short = function(modelValue, viewValue) {
if (ctrl.$isEmpty(modelValue)) {
return true;
}
if (modelValue.length >= 3) {
return true;
}
return false;
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<form name="form1">
<input type="password" name="password1" ng-model="pass1"><br>
<input type="password" my-dir confirm="pass1" name="password2" ng-model="pass2"><br>
<pre>{{form1.password2.$error | json}}</pre>
<p ng-show="form1.password2.$error.mismatch" style="color:red">Passwords are different</p>
</form>
</div>
If I change the first filed to:
<input type="password" my-dir confirm="pass2" name="password1" ng-model="pass1">
to validate in both directions, then scope.$eval(attrs.confirm) becomes undefined for both fields.
Here is a demo of my issue:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
});
app.directive('myDir', function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
ctrl.$validators.mismatch = function(modelValue, viewValue) {
// `scope.$eval(attrs.confirm)` always undefined
return viewValue === scope.$eval(attrs.confirm);
};
ctrl.$validators.short = function(modelValue, viewValue) {
if (ctrl.$isEmpty(modelValue)) {
return true;
}
if (modelValue.length >= 3) {
return true;
}
return false;
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<form name="form1">
<input type="password" my-dir confirm="pass2" name="password1" ng-model="pass1"><br>
<input type="password" my-dir confirm="pass1" name="password2" ng-model="pass2"><br>
<pre>{{form1.password2.$error | json}}</pre>
<p ng-show="form1.password2.$error.mismatch || form1.password.$error.mismatch" style="color:red">
Passwords are different
</p>
</form>
</div>
You need to do 2 things:
1. Add ng-model-options="{allowInvalid: true}" so invalid value will still update scope value.
2. Now you have problem that e.g. changing 2nd input wont trigger 1st re-validation. This is done using observe:
<body ng-controller="MainCtrl" ng-init="x = 0; y = 0">
<form name="form1">
<input type="password" my-dir="{{y}}" confirm="pass2" name="password1" ng-model="pass1" ng-model-options="{allowInvalid: true}"
ng-change="x = x + 1"><br>
<input type="password" my-dir="{{x}}" confirm="pass1" name="password2" ng-model="pass2" ng-model-options="{allowInvalid: true}"
ng-change="y = y + 1"><br>
and
attrs.$observe('myDir', function() {
ctrl.$validate();
});
http://plnkr.co/edit/ws4tVWGXfFNR2yqLRJN7?p=preview
P.S. for usual fields I would write my-dir="{{pass1}}" and then no need in $eval and ng-change, but for passwords... not sure
Clear number input type does not work for 'e' number
When I clear the input field with input eee in number type, it does not get cleared. Any other input numbers get cleared. Check the JSFiddle. Any hints would be appreciated.
http://jsfiddle.net/2ankx9up/
<div ng-app="app">
<div ng-controller="MainCtrl">
<input type="number" class="form-control" data-ng-model="searchAll">
</input>
<a class="clear" href="" data-ng-click="clearSearch()">X</a>
</div>
</div>
var app = angular.module("app", []);
app.controller("MainCtrl", function ($scope) {
$scope.searchAll = "";
$scope.clearSearch = function () {
$scope.searchAll = "";
};
});
The ng-model directive is unable to clear the content of an <input type="number"> element when that content parses to NaN (not a number). This can happen when a user pastes invalid content or simply types "eee".
One fix is to add a custom directive:
app.directive('clearNan', function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
ngModel.$formatters.push(function(value) {
if (!value) elem.val(null);
return value;
});
}
};
})
Usage:
<input type="number" clear-nan ng-model="x" />
The DEMO
angular.module('numfmt-error-module', [])
.directive('clearNan', function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
ngModel.$formatters.push(function(value) {
if (!value) elem.val(null);
return value;
});
}
};
})
.run(function($rootScope) {
$rootScope.typeOf = function(value) {
return typeof value;
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="numfmt-error-module">
<input clear-nan type="number" ng-model="x" />
<br>
{{ x }} : {{ typeOf(x) }}
<br>
<button ng-click="x=''">Clear input</button>
</body>
I have a problem with datetimepicker in AngularJS. When the page loaded, datetimepicker directive run, and I got the right value I want. But when I chose another date, directive does not work, although I have to change the event inside.
A few days ago, It worked, but not now. I tested many times. I don't know why, because I didn't change anything.
The code:
.directive("datetimeselect", [
"Config", function (Config) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, el, attr, ngModel) {
$(el).datetimepicker({
format: Config.defaultConfig.dateTimeFormat
});
el.on('dp.change', function (event) {
scope.$apply(function () {
var date = moment(event.date);
ngModel.$setViewValue(date.format(Config.defaultConfig.dateTimeFormat));
});
});
//format text from the user (view to model)
ngModel.$parsers.push(function (data) {
return moment(data).format(Config.defaultConfig.dateTimeFormat);
});
//format text going to user (model to view)
ngModel.$formatters.push(function (data) {
return moment(data).format(Config.defaultConfig.dateTimeFormat);
});
}
};
}
])
And this is HTML
<div class="form-group col-md-6">
<label for="ToTime" class="control-label">To Time</label>
<input type="text" class="form-control" name="ToTime" id="ToTime"
ng-model="record.ToTime" datetimeselect />
</div>
The $parser is unnecessary and the $formatter needs to set the date:
function postLink(scope, element, attr, ngModel) {
var ignoreChangeEvent = false;
element.datetimepicker();
element.on('dp.change', function(event) {
if (ignoreChangeEvent) {
ignoreChangeEvent = false;
return;
}
scope.$apply(function() {
ngModel.$setViewValue(event.date);
});
});
ngModel.$formatters.push(function (date) {
ignoreChangeEvent = true;
element.data("DateTimePicker").setDate(date);
});
}
The DEMO
angular.module('bootstrap-timepicker', [])
.directive('datetimepicker', [
function() {
return {
restrict: 'A',
link: postLink,
require: 'ngModel'
};
function postLink(scope, element, attr, ngModel) {
var ignoreChangeEvent = false;
element.datetimepicker();
element.on('dp.change', function(event) {
if (ignoreChangeEvent) {
ignoreChangeEvent = false;
return;
}
scope.$apply(function() {
ngModel.$setViewValue(event.date);
});
});
ngModel.$formatters.push(function (date) {
ignoreChangeEvent = true;
element.data("DateTimePicker").setDate(date);
});
}
}
])
.controller('IndexController', function($scope) {
$scope.date = new Date();
});
<link rel="stylesheet" href="//unpkg.com/bootstrap#3/dist/css/bootstrap.css">
<link rel="stylesheet" href="//unpkg.com/bootstrap#3/dist/css/bootstrap-theme.css">
<link rel="stylesheet" href="//unpkg.com/bootstrap-datetime-picker#2.3/css/bootstrap-datetimepicker.css">
<script src="//unpkg.com/jquery#2"></script>
<script src="//unpkg.com/bootstrap#3/dist/js/bootstrap.js"></script>
<script src="//unpkg.com/moment"></script>
<script src="//unpkg.com/eonasdan-bootstrap-datetimepicker#3/src/js/bootstrap-datetimepicker.js"></script>
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="bootstrap-timepicker">
<div class="container" ng-controller="IndexController">
<h4>Datetimepicker</h4>
<div class="form-group">
<div class='input-group date' datetimepicker ng-model="date">
<input type='text' class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
<p>
ng-model value: {{date}}
</p>
<div class='input-group date' datetimepicker ng-model="date">
<input type='text' class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
</body>
I would like to know if it is possible to send the user some kind of error message when the email already exist.
I have some directive like this. :
(function() {
angular
.module('app')
.directive('emailNotUsed',emailNotUsed);
emailNotUsed.$inject = ['$http', '$q'];
function emailNotUsed ($http, $q) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$asyncValidators.emailNotUsed = function(modelValue, viewValue) {
return $http.post('/email',viewValue).then(function(response) {
console.log(response);
return response.data == true ? /*$q.reject(response.data.errorMessage)*/ console.log("Show Error Message"): true;
// console.log(response);
});
};
}
};
}
}());
As you can see this returns Show error in the console. But is it possible to pass this value to the register controller ? And set the value to true or false ?
(function () {
'use strict';
angular
.module('app')
.controller('RegisterController', RegisterController);
RegisterController.$inject = ['UserService', '$http','$q' ];
function RegisterController(UserService, $http,$q) {
var vm = this;
vm.register = register;
function register() {
vm.dataLoading = true;
UserService.Create(vm.user)
}
}
})();
Or could i give an error messages directly into the html.
<div class="container" ng-controller="RegisterController as vm">
<div class="col-md-6 col-md-offset-3">
<div ng-show="vm.error" class="alert alert-danger">{{vm.error}}</div>
<form name="form" ng-submit="!form.$pending && form.$valid && vm.register()" role="form">
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" class="form-control" ng-model="vm.user.email" email-not-used ng-model-options="{ debounce: 500 }" required >
</div>
<div class="form-actions">
<button type="submit" ng-disabled="form.$invalid || vm.dataLoading" class="btn btn-primary">Register</button>
Cancel
</div>
</form>
</div>
</div>
My solution
After alot of struggling today i found a solution for my problem.
I modified the emailNotUsed directive to emailValidator.
(function() {
angular
.module('app')
.directive('emailValidator',emailValidator);
emailValidator.$inject = ['$q', '$timeout','$http'];
function emailValidator ($q, $timeout,$http){
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$asyncValidators.email = function(modelValue, viewValue) {
var deferred = $q.defer();
DoesEmailExist()
function DoesEmailExist () {
$http.post('/email',viewValue).then(function(response) {
if (response.data==true) {
console.log('Email does exist')
deferred.reject();
}else {
deferred.resolve();
}
});
};
console.log(deferred.promise)
return deferred.promise;
};
}
}
}
}());
And in registration.html i added this :
<div class="form-group" ng-class="{ 'has-error': form.email.$dirty && form.email.$invalid }">
<label for="email"> Email</label>
<input type="text" ng-model="signup.email" ng-model-options="{ debounce: 500 }" name="email" id="email" class="form-control" required email-validator>
<div class="help-block" ng-messages="form.email.$error" ng-if="form.email.$dirty">
<p ng-message="required">Your name is required.</p>
<div ng-message="email" class="help-block" >email already in use</div>
</div>
Yes you can directly show error message into the HTML.
try this,
In HTML,
<form name="form" ng-submit="!form.$pending && form.$valid && vm.register()" role="form">
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" class="form-control" ng-model="vm.user.email" email-not-used ng-model-options="{ debounce: 500 }" required >
<span ng-show="form.email.$invalid && form.email.$dirty">Email is already in use!</span>
<span ng-show="form.email.$valid && form.email.$dirty">Email available!</span>
</div>
<div class="form-actions">
<button type="submit" ng-disabled="form.$invalid || vm.dataLoading" class="btn btn-primary">Register</button>
Cancel
</div>
</form>
In app.js,
var app = angular.module('app', []);
app.directive('emailNotUsed',emailNotUsed);
emailNotUsed.$inject = ['$http', '$q'];
function emailNotUsed ($http, $q) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$asyncValidators.emailNotUsed = function(modelValue, viewValue) {
var data='';
if (viewValue=='abc#xyz.com'){
ngModel.$setValidity('valid', false);
scope.form.email.$setValidity('email', false);
}else{
ngModel.$setValidity('valid', true);
scope.form.email.$setValidity('email', true);
}
return data;
}
}
}
}
app.controller('RegisterController', RegisterController);
RegisterController.$inject = ['$scope', '$http','$q' ];
function RegisterController($scope, $http,$q) {
var vm = this;
$scope.name = 'abc#xyz.com email is already in use ';
vm.register = register;
function register() {
vm.dataLoading = true;
UserService.Create(vm.user)
}
}
Hope this helps. .
You can setup error message directly in your view. Bind to $scope.form.email.emailNotUsed.
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" class="form-control" ng-model="vm.user.email" email-not-used ng-model-options="{ debounce: 500 }" required >
<span ng-show="form.email.emailNotUsed">Email is already in use!</span>
</div>
Recently, I had a problem with Angular form validity. I easy can to add the entry to Array with help ngModel.$setValidity, but I can't to remove it after. My html tag has a lot of valid/invalid classes. I'd tried to do the form to pristine. But it does't work. How that things to do generally? Help me please! (Sorry for my english =) if I've made a mistake somewhere.)
It's not terribly well documented, but you can actually pass in null to the $setValidity() function in order to completely clear a validation flag.
If you want to set it to be valid then simply pass in true
//Reset validity for this control
this.form.firstName.$setValidity('someValidator', null);
//Or set to valid
this.form.firstName.$setValidity('someValidator', true);
And here is a running snippet to demonstrate this technique.
(function() {
'use strict';
function MainCtrl() {
this.firstName = 'Josh';
}
MainCtrl.prototype = {
setInvalid: function(ctrl) {
ctrl.$setValidity('customValidator', false);
},
setPristine: function(ctrl) {
ctrl.$setValidity('customValidator', null);
},
};
angular.module('sample', [])
.controller('MainCtrl', MainCtrl);
}());
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" />
<div class="container" ng-app="sample" ng-controller="MainCtrl as ctrl">
<div class="row">
<div class="col-sm-12">
<form name="ctrl.form">
<div class="form-group" ng-class="{'has-error':ctrl.form.firstName.$invalid}">
<label class="control-label">First Name</label>
<input type="text" class="form-control" name="firstName" ng-model="ctrl.firstName" />
</div>
<button type="button" class="btn btn-danger" ng-click="ctrl.setInvalid(ctrl.form.firstName)">Set Invalid</button>
<button type="button" class="btn btn-success" ng-click="ctrl.setPristine(ctrl.form.firstName)">Set Valid</button>
</form>
</div>
</div>
</div>
(function () {
angular.module("App")
.directive("password", password);
function password() {
var lastTrueRegex = {};
var regexes = {
week: /(?=^.{8,}$).*$/,
pettyWeek: /(?=^.{8,}$)(?=.*\d).*$/,
normal: /(?=^.{8,}$)(?=.*\d)(?=.*[a-z]).*$/,
prettyStrong: /(?=^.{8,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).*$/,
strong: /(?=^.{8,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?![.\n]).*$/,
veryStrong: /(?=^.{8,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?![.\n])(?=.*[!##$%^&*]+).*$/
};
function forEach(data, callback) {
for (var key in data) {
if (data.hasOwnProperty(key)) {
callback(key, data[key]);
}
}
}
return {
require: "ngModel",
restrict: 'EA',
link: function (scope, element, attributes, ngModel) {
ngModel.$parsers.unshift(function (textValue) {
forEach(regexes, function (key, regex) {
if (regex.test(textValue)){
lastTrueRegex.name = key;
lastTrueRegex.value = true;
}else{
ngModel.$setValidity(key, null);
}
});
if (lastTrueRegex.name){
ngModel.$setValidity(lastTrueRegex.name, lastTrueRegex.value);
return textValue;
}
});
ngModel.$formatters.unshift(function (modelValue) {
//ngModel.$setValidity('strongPass', isValid(modelValue));
return modelValue;
});
}
};
}
})();
<form name="myForm">
<input type="text" name="password" ng-model="textValue" password/>
</form>