angularJs Form validation with dynamic error message - javascript

I want validate a form where for each input may have many error messages as per input. I want to write those error message from javaScript.
HTML
<input name="username" ng-model="user.username" type="text" placeholder="Username" required>
<div class="field-message" ng-messages="frm.username.$error" ng-if='frm.username.$dirty' ng-cloak>
<div ng-message="required">Username is required</div>
</div>
JS
angular.module('DemoApp', ['ngMessages']);
angular.bootstrap(document.getElementById('demoApp'), ['DemoApp']);
In the above code ng-message directive have error message that will be shown if input is not valid. If there are so many case to validate and each case have different error message then multiple ng-message have to add in html for particular input control.
I want to only input tag in html and error message to be come from javascript.
How can I achieve this?

See this question: How to add custom validation to an AngularJS form?
To summarize:
You can use ui-validate
OR
You can just create custom validation, like so (copied from the answer in the original question):
app.directive('blacklist', function (){
return {
require: 'ngModel',
link: function(scope, elem, attr, ngModel) {
var blacklist = attr.blacklist.split(',');
//For DOM -> model validation
ngModel.$parsers.unshift(function(value) {
var valid = blacklist.indexOf(value) === -1;
ngModel.$setValidity('blacklist', valid);
return valid ? value : undefined;
});
//For model -> DOM validation
ngModel.$formatters.unshift(function(value) {
ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
return value;
});
}
};
});
And here's some example usage:
<form name="myForm" ng-submit="doSomething()">
<input type="text" name="fruitName" ng-model="data.fruitName" blacklist="coconuts,bananas,pears" required/>
<span ng-show="myForm.fruitName.$error.blacklist">
The phrase "{{data.fruitName}}" is blacklisted</span>
<span ng-show="myForm.fruitName.$error.required">required</span>
<button type="submit" ng-disabled="myForm.$invalid">Submit</button>
</form>
EDIT: According to OP's request, print only the relevant error:
Plunkr: http://plnkr.co/edit/KN0Oukb8uACvZjtGPUg4?p=preview
var app = angular.module('angularjs-starter', []);
app.controller('MainCtrl', function($scope) {
$scope.doSomething = function() {
alert('Submitted!');
}
$scope.getErrors = function() {
if ($scope.myForm.fruitName.$error.required) {
return 'This field is required';
} else if ($scope.myForm.fruitName.$error.blacklist) {
return 'The phrase ' + $scope.data.fruitName + ' is blacklisted';
}
}
});
app.directive('blacklist', function() {
return {
require: 'ngModel',
link: function(scope, elem, attr, ngModel) {
var blacklist = attr.blacklist.split(',');
ngModel.$parsers.unshift(function(value) {
ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
return value;
});
}
};
});
.invalid {
color: red;
}
.ng-invalid {
border-color: red;
}
<!DOCTYPE html>
<html ng-app="angularjs-starter">
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<form name="myForm" ng-submit="doSomething()">
<div>Please <u>don't</u> type the words: coconuts, bananas or pears.</div>
<input type="text" name="fruitName" ng-model="data.fruitName" blacklist="coconuts,bananas,pears" required/>
<span class="invalid" ng-show="myForm.fruitName.$error">{{getErrors()}}</span>
<br/>
<button type="submit" ng-disabled="myForm.$invalid">Submit</button>
<button type="button" ng-click="getErrors()">Get errors</button>
</form>
</body>
</html>

Related

Submit form when async validator returns promise in AngularJS

I have created a async validator 'username-validator' to check username availability and to avoid number of remote calls I am updating ng-model on 'blur' event rather than on 'key press'. So validator is triggered only on 'blur' event.
<input type="text" ng-model="signup.username" name="username"
required
username-validator
ng-model-options="{ updateOn: 'blur' }">
But when form gets submitted it checks if form is valid and only then it gets submitted.
$scope.submit = function(form) {
if (form.$valid) {
alert('submitting', $scope.signup.username, $scope.signup.password);
}
};
But when I am clicking on Submit button and if form is in $pending state then form doesn't get submitted in one click, because at the time of click it is in $pending state and $valid is undefined.
I want to write my $scope.submit function to handle submission once $pending state is resolved.
How do i do it?
Don't want to disable the Submit button for $pending state.
To demonstrate the problem I am adding running snippet with it.
angular.module('app', ['ngMessages'])
.controller('MainCtrl', function($scope) {
$scope.signup = {};
$scope.submit = function(form) {
if (form.$valid) {
alert('submitting ' + $scope.signup.username + ' ' + $scope.signup.password);
}
};
})
.directive('usernameValidator', function($q, $timeout) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$asyncValidators.username = function(modelValue, viewValue) {
if (!viewValue) {
return $q.when(true);
}
var deferred = $q.defer();
$timeout(function() {
// Faking actual server-side validity check with $http.
// Let's pretend our service is so popular all short username are already taken
if (viewValue && viewValue.length < 5) {
deferred.reject();
}
deferred.resolve();
}, 2000);
return deferred.promise;
};
}
};
});
.validation-error {
color: red;
}
.validation-pending {
color: orange;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular-messages.min.js"></script>
<script src="script.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body ng-app="app">
<div ng-controller="MainCtrl">
<form name="myForm" ng-submit="submit(myForm)" novalidate>
<div>
<label>
Username:
<input type="text" ng-model="signup.username" name="username" required username-validator ng-model-options="{ updateOn: 'blur' }">
</label>
<div ng-if="myForm.username.$dirty">
<div ng-messages="myForm.username.$error" class="validation-error">
<div ng-message="required">Username required</div>
<div ng-message="username">Username already in use</div>
</div>
<div ng-messages="myForm.username.$pending" class="validation-pending">
<div ng-message="username">Checking username availability...</div>
</div>
</div>
</div>
<div>
<label>
Password:
<input type="password" ng-model="signup.password" name="password" required>
</label>
<div ng-messages="myForm.password.$error" ng-if="myForm.password.$dirty" class="validation-error">
<div ng-message="required">Password required</div>
</div>
</div>
<button type="submit">Submit</button>
</form>
</div>
</body>
</html>
You could attach the promise to the scope when checking for the username
scope.usernameCheck = deferred.promise;
return deferred.promise;
Then in your submit function wait for the promise to be resolved
$scope.submit = function(form) {
if ($scope.usernameCheck) {
$scope.usernameCheck.then(function() {
if (form.$valid) {
alert('submitting ' + $scope.signup.username + ' ' + $scope.signup.password);
}
});
}
};

Angular UI datepicker validated as false when empty

I'm using the JqueryUI datepicker(1.0.0) directive with Angular 1.4.9 to display a date of birth. This field is not required and should not be validated unless it's filled.
Once the page loads, the field is validated as true(As expected). And once the user selects a date, it's again valid. But if we were to manually erase the field, the field becomes invalid.
<input ui-date="dateOptions" name="dateOfBirth" ng-model="dob"/>
ng-model can be set to the same value before and after, but the value remains invalid.
I've created a JSFiddle that replicates the problem here.
https://jsfiddle.net/nipuna777/ctsmuv80/
As said #nipuna777, it looks like a bug in the datepicker.
We can fix it with a directive.
Live example on jsfiddle.
var myApp = angular.module('myApp', ['ui.date']);
myApp.directive('uiDateFix', function($timeout) {
return {
restrict: "A",
require: 'ngModel',
priority: 100,
scope: {
ngRequired: "="
},
link: function(scope, elem, attr, ngModel) {
var originalValidate = null;
$timeout(function() {
if (!originalValidate)
originalValidate = ngModel.$validators.uiDateValidator;
ngModel.$validators.uiDateValidator = function uiDateValidator2(modelValue, viewValue) {
//Define correct validations
if (viewValue || scope.ngRequired)
return originalValidate(modelValue, viewValue);
else
return true;
}
}, 500);
}
}
});
//myApp.factory('myService', function() {});
myApp.controller('MyCtrl', function($scope) {
$scope.name = 'Superhero';
$scope.value = 'Superhero';
$scope.dateOptions = {
changeYear: true,
changeMonth: true,
yearRange: '1900:-0'
};
$scope.isReq = true;
$scope.dob = ""
})
<script src="https://code.jquery.com/jquery-2.2.1.js" charset="utf-8"></script>
<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.9/angular.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-date/1.0.0/date.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.11.3/themes/smoothness/jquery-ui.css">
<body ng-app="myApp">
<div ng-controller="MyCtrl">
<h1> Datepicker</h1>
<form name="person">
<label for="dateOfBirth">Date of Birth: </label>
<input ui-date="dateOptions" name="dateOfBirth" ng-model="dob" ui-date-fix ng-required="isReq" />
<label for="isReq">Required </label>
<input type="checkbox" name="isReq" ng-model="isReq" />
<p>
dob value: {{dob}}
</p>
<p>
dob valid: {{person.dateOfBirth.$valid}}
</p>
<p>
dob errors: {{person.dateOfBirth.$error}}
</p>
<hr>
<h2>Recreating the problem</h2>
<ol>
<li>With the field empty dob is valid</li>
<li>When you select a value from datepicker again the dob field is valid</li>
<li>When dob is manually removed, the field becomes invalid</li>
</ol>
</form>
</div>
</body>
It is an issue in the validator of the plugin, it is fixed in the master branch/dist/date.js.
So you can use that file instead of the one in the cdn to fix it

How to do auto tooltip validation msg?

Here I created sample file for validation, which is working fine...
But My requirement is I need to do some modification on that while validating. Error message need to show in auto tooltip. It needs to be shown automatically when there is error and hide automatically once error cleared. Until error clear popup need to be stay.
If it is possible without jquery or else with jquery also fine.
var app = angular.module('myapp', ['UserValidation']);
myappCtrl = function($scope) {
$scope.formAllGood = function () {
return ($scope.usernameGood && $scope.passwordGood && $scope.passwordCGood)
}
}
angular.module('UserValidation', []).directive('validUsername', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
// Any way to read the results of a "required" angular validator here?
var isBlank = viewValue === ''
var invalidChars = !isBlank && !/^[A-z0-9]+$/.test(viewValue)
var invalidLen = !isBlank && !invalidChars && (viewValue.length < 5 || viewValue.length > 20)
ctrl.$setValidity('isBlank', !isBlank)
ctrl.$setValidity('invalidChars', !invalidChars)
ctrl.$setValidity('invalidLen', !invalidLen)
scope.usernameGood = !isBlank && !invalidChars && !invalidLen
})
}
}
}).directive('validPassword', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
var isBlank = viewValue === ''
var invalidLen = !isBlank && (viewValue.length < 8 || viewValue.length > 20)
var isWeak = !isBlank && !invalidLen && !/(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z])/.test(viewValue)
ctrl.$setValidity('isBlank', !isBlank)
ctrl.$setValidity('isWeak', !isWeak)
ctrl.$setValidity('invalidLen', !invalidLen)
scope.passwordGood = !isBlank && !isWeak && !invalidLen
})
}
}
}).directive('validPasswordC', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue, $scope) {
var isBlank = viewValue === ''
var noMatch = viewValue != scope.myform.password.$viewValue
ctrl.$setValidity('isBlank', !isBlank)
ctrl.$setValidity('noMatch', !noMatch)
scope.passwordCGood = !isBlank && !noMatch
})
}
}
})
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/css/bootstrap-combined.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp">
<form name="myform" class="form form-horizontal" ng-controller="myappCtrl" novalidate>
<legend>Angular User Validation with Bootstrap Decorations</legend>
<div class="control-group" ng-class="{error:!myform.username.$valid}">
<label for="inputUsername" class="control-label">Username:</label>
<div class="controls">
<input type="text" id="inputUsername" name="username" ng-model="username" valid-username />
<div class="help-inline">
<span ng-show="!!myform.username.$error.isBlank">Username Required.</span>
<span ng-show="!!myform.username.$error.invalidChars">Username must contain letters & spaces only.</span>
<span ng-show="!!myform.username.$error.invalidLen">Username must be 5-20 characters.</span>
</div>
</div>
</div>
<div class="control-group" ng-class="{error:!myform.password.$valid}">
<label for="inputPassword" class="control-label">Password:</label>
<div class="controls">
<input type="text" id="inputPassword" name="password" ng-model="password" valid-password />
<div class="help-inline">
<span ng-show="!!myform.password.$error.isBlank">Password Required.</span>
<span ng-show="!!myform.password.$error.isWeak">Must contain one upper & lower case letter and a non-letter (number or symbol.)</span>
<span ng-show="!!myform.password.$error.invalidLen">Must be 8-20 characters.</span>
</div>
</div>
</div>
<div class="control-group" ng-class="{error:!myform.password_c.$valid}">
<label for="password_c" class="control-label">Confirm Password:</label>
<div class="controls">
<input type="text" id="password_c" name="password_c" ng-model="password_c" valid-password-c />
<div class="help-inline">
<span ng-show="!!myform.password_c.$error.isBlank">Confirmation Required.</span>
<span ng-show="!!myform.password_c.$error.noMatch">Passwords don't match.</span>
</div>
</div>
</div>
<div class="form-actions" ng-show="formAllGood()">
<input type="submit" class="btn btn-primary" value="Submit" />
</div>
</form></div>
Yes it is possible to show/hide popover on any event. Following code depicts a validate function for numbers using popover.
JSFiddle.
function validate(el) {
var regex = /^\d+$/g;
var valid = regex.test(el.value);
if (!valid) {
// Check if popover is already visible to handle flicker effect.
if ($("#txtInput").next('div.popover').length == 0) {
$('#txtInput').popover({
placement: 'bottom',
content: 'This is not a valid entry'
}).popover('show');
}
} else {
$('#txtInput').popover('hide');
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<input type="text" id="txtInput" onkeyup="validate(this)">
References:
Check visibility
Documentation.
Best solution is to use Parsley.js
Why Parsley :
Validation Event can be customized (onsubmit , onkeyup , or any other event )
Validation Style can be customized
Validation message will automatically disappear , once input satisfies conditions
Check their examples

How to call custom directive on form submit

I have a custom directive which works when I once change input value but when I submit form without changing input field value directive does not display error message.
html code:
<form name="myform" role="form" class="form-horizontal" method="post" novalidate>
<div class="form-group">
<label class="control-label col-sm-3" for="firstName">First name :</label>
<div class="col-sm-9">
<input name='firstName' class="form-control" type='text' required ng-model='name' string>
</div>
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-success" />
</div>
</form>
directive :
validationModule.directive('string', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attr, ctrl) {
function validationError(value) {
if (value.length < 1) {
ctrl.$setValidity('required', false);
//showPopOver(element, "Required field");
errorValidation(element, "Required field");
// $(element).popover('show');
}
if (/[a-zA-Z]/.test(value)) {
ctrl.$setValidity('invalid', true);
// $(element).popover('destroy');
successValidation(element);
}
if (/[0-9]/.test(value)) {
ctrl.$setValidity('invalid', false)
errorValidation(element, "number not allowed");
// showPopOver(element, "number not allowed");
// $(element).popover('show');
}
return value;
}
ctrl.$parsers.push(validationError);
}
};
});
How can I call directive to perform validation on submit event ?
I don't know which version of angular you are using, but since 1.3 validators are not hacked into parsers. See $validators in https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
Also using such generic names for directives is discouraged due to possible name collisions. Use some prefix eg myString

AngularJS watch not being triggered

I'm having a problem with AngularJs. I have created a directive that $watch the model and it takes some action based on the model's current status. However, although during debugging I can see that the $watch is set, it is only being triggered after the model get valid at least once and I don't know why that is happening. Debugging it doesn't even gets into the $watch function when something is typed.
The code is below:
Directive:
(function() {
'use strict';
var app = angular.module('app');
app.directive('tooltipValidation', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var tooltip = $(element).qtip({
content: {
text: element.next('div')
},
show: false,
hide: true
}).qtip('api');
scope.$watch(attrs.ngModel, function() {
if (ngModel.$invalid && ngModel.$dirty) {
tooltip.show();
} else {
tooltip.hide();
}
});
}
};
});
})()
HTML:
<div class="form-address clearfix" ng-show="isNewShippingAddress" ng-form="newShippingAddressForm">
<h3>Include new shipping address:</h3>
<div class="novo-endereco clearfix" id="newAddress">
<div class="required address apelido">
<label for="newShippingAddressAlias">Alias</label>
<input id="newShippingAddressAlias" name="newShippingAddressAlias" type="text" tooltip-validation ng-model="newShippingAddress.Alias" required ng-maxlength="32" />
<div data-ng-show="newShippingAddressForm.newShippingAddressAlias.$dirty && newShippingAddressForm.newShippingAddressAlias.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressAlias.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressAlias.$error.maxlength">max 32 char</p>
</div>
</div>
<div class="required endereco">
<label for="newShippingAddressStreet">Street</label>
<input id="newShippingAddressStreet" name="newShippingAddressStreet" type="text" tooltip-validation ng-model="newShippingAddress.Street" required ng-maxlength="256" />
<div data-ng-show="newShippingAddressForm.newShippingAddressStreet.$dirty && newShippingAddressForm.newShippingAddressStreet.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressStreet.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressStreet.$error.maxlength">max 256 char</p>
</div>
</div>
<div class="required cep">
<label for="newShippingAddressZipCode">ZipCode</label>
<input id="newShippingAddressZipCode" name="newShippingAddressZipCode" type="text" tooltip-validation ng-model="newShippingAddress.ZipCode" required ng-pattern="/^[0-9]{8}$/" />
<div data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$dirty && newShippingAddressForm.newShippingAddressZipCode.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$error.pattern">8 digits</p>
</div>
</div>
<input type="submit" class="button grey" value="Save new address" data-ng-click="saveShippingAddress()" ng-disabled="newShippingAddressForm.$invalid" />
</div>
</div>
Regards,
dimello
Try:
scope.$watch(function(){
return ngModel.$viewValue; //Watch for view value (the value in your input)
}, function() {
if (ngModel.$invalid && ngModel.$dirty) {
tooltip.show();
} else {
tooltip.hide();
}
});
DEMO
Explanation:
When you type an invalid value into the input with ng-model, the underlying model is not updated, causing your scope.$watch(attrs.ngModel not being fired because you're watching for changes in the model. If you need to fire the function every time the input changes no matter it's valid or not, try the above solution.

Categories

Resources