I have a form with currently one field, which has few validation rules:
<form name="my_form" novalidate ng-controller="FormController">
<label>Your Name:</label>
<input type="text"
name="name"
placeholder="Your Name"
ng-model="form.name"
ng-minlength="3"
ng-maxlength="20"
unique
required />
<button ng-click="submitForm()">Submit</button>
<div class="error"
ng-show="my_form.isSubmitted"
ng-messages="my_form.name.$error">
<div ng-messages-include="errors.html"></div>
</div>
</form>
My field is validated against:
Min. length;
Max. length;
It's required
And must be unique (custom validation rule)
I'm using ng-messages to display error messages near input field. Here is my errors.html template:
<div ng-message="required">This field is required.</div>
<div ng-message="minlength">This field is too short.</div>
<div ng-message="maxlength">This field is too long.</div>
<div ng-message="unique">The value of this field must be unique.</div>
The validation should be started only after 'Submit' button is pressed (submitForm() function sets my_form.isSubmitted flag to true and my error div is displayed)
Here is my js code:
var app = angular.module('formValidation', ['ngMessages']);
app.controller('FormController', function($scope) {
$scope.submitForm = function() {
$scope.my_form.isSubmitted = true;
};
});
app.directive('unique', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, ele, attrs, ctrl) {
var names = ['Dmitry', 'Alexander', 'Elizabeth'];
ctrl.$parsers.push(function(value) {
if (names.indexOf(value) > -1) {
ctrl.$setValidity('unique', false);
return false;
}
ctrl.$setValidity('unique', true);
return true;
});
}
};
);
Everything works fine, but what I want to do now is to hide errors if the field is modified after errors were shown (until submit button will be pressed again).
The first idea came to my mind is to add another condition to ng-show directive of error div to check if corresponding field is updated and if it is, errors should not be shown. Something like:
<div class="error"
ng-show="!my_form.name.isUpdated && my_form.isSubmitted"
ng-messages="my_form.name.$error">
<div ng-messages-include="errors.html"></div>
</div>
So, on button click I can set isUpdated flag of all form fields to false and on input update can set it to true. But this solution seems to me far from elegant. I'm sure there is a better way to achieve this behaviour. Any ideas?
My current solution (probably not the best one):
<input type="text"
name="name"
placeholder="Your Name"
ng-model="form.name"
ng-minlength="3"
ng-maxlength="20"
unique
updatable
required />
<button ng-click="submitForm()">Submit</button>
<div class="error"
ng-show="!my_form.name.isDirty && my_form.isSubmitted"
ng-messages="my_form.name.$error">
<div ng-messages-include="errors.html"></div>
</div>
I added new directive updatable to my field and updated the show condition for error div:
ng-show="!my_form.name.isDirty && my_form.isSubmitted"
The directive:
app.directive('updatable', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, ele, attrs, ctrl) {
ele.bind('input', function() {
scope.$apply(function() {
ctrl.isDirty = true;
});
);
}
};
});
And a small update of submitForm function which now sets isDirty flag of my field(s) to false:
$scope.submitForm = function() {
$scope.my_form.isSubmitted = true;
$scope.my_form.name.isDirty = false;
};
Related
I'm using Angular JS v1.5.5 and defined a custom input field type currency that allows entering large numbers as e.g. 1.5k or 3.4M or 4T etc. This I accomplished defining a custom directive currency. The problem is that even when the display value is shown and the model contains a valid value, and when pressing form submmit I get the You must enter a valid value error.
This is the input field configuration as shown using the IE DOM explorer:
<div class="input-group">
<input name="tradeParameters.tradeNotional" class="form-control ng-not-empty ng-valid ng-valid-required ng-dirty ng-valid-number ng-touched" id="tradeParameters_tradeNotional" required="required" type="number" step="1.0E-5" value="" ng-model="inSampleFormData.tradeParameters.tradeNotional" ng-required="true" currency="">
<span class="input-group-addon">$</span>
</div>
The MainModule.ts defines the filter and directive as follows:
.filter('identity', ['$filter', function ($filter) {
return function(input) {
return input;
};
}])
.directive("currency", function($filter, formatServices: FormatServices){
var p = function(viewValue){
if (angular.isDefined(viewValue)) {
return $filter('identity')(formatServices.currParse(viewValue));
}
};
var f = function(modelValue){
if (angular.isDefined(modelValue)) {
return $filter('identity')(formatServices.currFormat(modelValue));
}
};
return {
require: 'ngModel',
link: function(scope, ele, attr, ctrl){
ctrl.$parsers.unshift(p);
ctrl.$formatters.unshift(f);
}
};
})
I'm writing a directive to dynamically hide input field.Rules are coming from the database.If all the divs within a div are hidden then paren div's visibility is also set to none. Problem is hidden elements are not passing validation.
This is my code
return {
restrict: 'A',
priority: 0,
terminal: false,
link: function (scope, element, attrs) {
var unwatch = scope.$watchCollection('fieldRules', function (newVal, oldVal) {
if (scope.fieldRules != undefined && scope.fieldRules.length > 0) {
var elem = $(element);
var rule = $filter('filter')(scope.fieldRules, { FieldKey: attrs.key })[0];
if (element.is('div')) {
if (!rule.IsVisible) {
attrs.$set('ngHide', true);
//attrs.$set('ngIf', false);
var lastDiv = elem.siblings('div').last();
lastDiv.addClass('field-alignment');
}
else {
attrs.$set('ngShow', true);
//attrs.$set('ngIf', true);
}
var parentDiv = elem.closest('div.form-group')
if (parentDiv.find('div.input-group[ng-hide!=true]').length === 0){
parentDiv.hide();
}
}
attrs.$set('fieldRule', null);
$compile(element)(scope);
}
})
}
}
One of the solutions on stackoverflow advised to use ng-if but it also did not work.
Edit
I want angular to bypass validation for input fields which are inside a hidden div(these input fields are required means they contain attribute ng-required=true ). I have also removed the unnecessary code.
This is my html
<div field-rule data-key="minhourlyrate" class="input-group erorinner" style="width: 48%;float: left;" ng-class="{invalidField:checkFieldValidity(signupForm.minhourlyrate)}">
<span class="input-group-addon"><i class="fa fa-usd"></i></span>
<input id="txtMinHourlyRate" min="0" max="100" type="number" name="minhourlyrate" ng-model="user.MinHourlyRate" class="form-control" placeholder="Min" autofocus="" ng-pattern="/^[0-9]+(\.[0-9]{1,2})?$/" step="0.01" ng-required="true">
All I'm trying to do is hide an input field along with the div containing it through a directive.An idea to write a new directive to solve the problem is also very welcome.
I would like to make my service be able to handle any input field. At the moment I am manually writing everything and it's starting to amount to a lot of manual work. Is there a way to send the element object when the elements ng-change property is invoked? I can then change the elements ng-class as a result of that.
Html:
<input type="text" id="email" data-ng-model="email" data-ng-change="changeEmail()" placeholder="your email here" data-ng-class="emailFormControlColor">
In the controller:
$scope.changeEmail = function () {
if ($checkInput.checkEmail($scope.email)) {
// email input is good
$scope.emailFormControlColor = 'form-control-success'; // change from error to success
} else {
// email input is bad
if ($scope.emailFormControlColor === 'form-control-success')
$scope.emailFormControlColor = 'form-control-error'; // change from success to error
}
};
The service (this is included in the controller arguments ofc.):
.service('checkInput', ['$controller', '$window', '$location', function ($controller, $window, $location) {
return {
checkEmail: function (email) {
// <--- I would like to update the ng-class of the focused element here! This would result in me not manually having to write code for each input!
var regex = /^[^\s#]+#[^\s#]+\.[^\s#]+$/;
return regex.test(email);
}
};
}])
The code above is how i have it now. As you can see I'm manually changing the $scope.emailFormControlColor.
Imagine I have three input fields:
<input type="text" id="email1" data-ng-model="email1" data-ng-change="changeEmail()" placeholder="your email here" data-ng-class="emailFormControlColor1">
<input type="text" id="email2" data-ng-model="email2" data-ng-change="changeEmail()" placeholder="your email here" data-ng-class="emailFormControlColor2">
<input type="text" id="email3" data-ng-model="email3" data-ng-change="changeEmail()" placeholder="your email here" data-ng-class="emailFormControlColor3">
How could I write my service so that I do not have the write the following manually:
$scope.emailFormControlColor1 = 'form-control-success';
$scope.emailFormControlColor2 = 'form-control-success';
$scope.emailFormControlColor3 = 'form-control-success';
I hope my question is clear, otherwise say and I'll update it!
I think using a directive to solve this problem is more desirable. For one, it's considered an anti-pattern to modify the DOM in a controller or a service.
The other reason is that when you use a directive, you will be provided the DOM element that the directive was used on. Here's some untested code that you might use as a starting point:
myModule.directive('checkEmail', function() {
require: 'ngModel',
link: function(scope, element, attributes, ngModelController) {
// require that ng-model is used on this element, so you can hook
// into the Angular validator pipeline ...
ngModelController.validators.checkEmail = function(modelValue, viewValue) {
// now return true or false if viewValue is considered valid
var regex = /^[^\s#]+#[^\s#]+\.[^\s#]+$/;
var isValid = regex.test(viewValue);
// but you also have an opportunity to do your DOM manipulation
element.toggleClass('form-control-success', isValid);
element.toggleClass('form-control-error', !isValid);
return isValid;
});
}
});
Instead of applying the CSS class as above, you could just use Angular's built in validation system. Your directive code is the same as above, except you don't apply any CSS classes in the directive. When the validation code in the directive returns false, Angular will make the field invalid ... and you can use this fact to apply the CSS in your HTML:
<form name="myForm">
<input ng-model="whatever"
name="email"
check-email
ng-class="{'form-control-success': myForm.email.$valid, 'form-control-error': myForm.email.$invalid}">
</form>
The above applies the CSS anytime the field is invalid, but you can also apply it when a specific validation error occurs, in this cased we named the validator "checkEmail", so this also would work:
<form name="myForm">
<input ng-model="whatever"
name="email"
check-email
ng-class="{'form-control-success': !myForm.email.$error.checkEmail, 'form-control-error': myForm.email.$error.checkEmail}">
</form>
I would create an object that contains all of the information you need for each email. Something like this:
HTML:
<input type="text" id="email0" data-ng-model="emails[0].value" data-ng-change="changeEmail(emails[0])" placeholder="your email here" data-ng-class="emails[0].class">
<input type="text" id="email1" data-ng-model="emails[1].value" data-ng-change="changeEmail(emails[1])" placeholder="your email here" data-ng-class="emails[1].class">
<input type="text" id="email2" data-ng-model="emails[2].value" data-ng-change="changeEmail(emails[2])" placeholder="your email here" data-ng-class="emails[2].class">
JavaScript:
$scope.emails = [
{ value: '', class: '' },
{ value: '', class: '' },
{ value: '', class: '' },
];
$scope.changeEmail = function (email) {
if ($checkInput.checkEmail(email)) {
// email input is good
email.class = 'form-control-success'; // change from error to success
} else {
// email input is bad
if (email.class === 'form-control-success') {
email.class = 'form-control-error'; // change from success to error
}
}
};
If appropriate, you could use something like ng-repeat to avoid copy/paste in the HTML.
Even though Sunil D. pointed me basically to the answer the code is incorrect. Down below is the code that makes it work. http://codepen.io/basickarl/pen/MyoZNB
HTML:
<div ng-app="app" ng-controller="ctrl">
<form name="myForm">
<input ng-model="name" name="name">
<input ng-model="email" name="email" check-email>
</form>
</div>
CSS:
input {
border: 5px;
border-style: solid;
border-color: silver;
}
.input-invalid {
border-color: red;
}
.input-valid {
border-color: lime;
}
JS:
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function($scope) {
$scope.name = "";
$scope.email = "";
}]);
app.directive('checkEmail', [function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, el, attr, ctrl) {
ctrl.$validators.checkEmail = function(modelVal, viewVal) {
var regex = /^[^\s#]+#[^\s#]+\.[^\s#]+$/;
var isValid = regex.test(viewVal);
el.toggleClass('input-valid', isValid);
el.toggleClass('input-invalid', !isValid);
return isValid;
}
}
}
}]);
I'm a beginner with AngularJS and for now I'm trying to create a registration form.
I tried to follow the instructions I found on the internet, but it looks like I cannot get my validation triggered. I'm not sure what is wrong.
Here's my JS for the validation:
var login = angular.module('login', []);
login.directive('repeatedValue', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$validators.repeatedValue = function (modelValue, viewValue) {
alert("validation");
return false;
};
}
};
});
and the input field looks like this:
<input type="password" class="form-control" id="password2"
placeholder="Repeat password" value="" tabindex="3" ng-model="login.registerPassword2" repeatedValue/>
still, for some reason, even if I start typing into the field, the validation is not triggered and as a result I can still submit the form.
you must use right directive name in template (dash-delimited)
<input type="password" ... repeated-value/>
See Normalization chapter in docs https://docs.angularjs.org/guide/directive
I have a form with input fields. One input field allows alphanumeric values. But if the value contains letters, then the letters have to be uppercase. How can I implement this approach? Is it possible to define this in the input field, when the user does an input then the letters are automatically shown in uppercase?
The View:
<div class="form-group-sm has-feedback">
<label class="control-label" for="article-id">Article Nr.</label>
<input type="text"
class="form-control"
name="Article"
id="article-id"
ng-model="selected.article"
ng-pattern="/^[a-zA-Z]{2}[a-zA-Z\d]{9}[0-9]$/"
ng-required="true"
/>
</div>
...
//the other inputs are the same definition
It is important for me to save the value in the DB with uppercase letters.
Just create a simple directive ,this answer is based on the answer here: Angular.js: How to autocapitalize an input field?.
myApp.directive('capitalize', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
var capitalize = function(inputValue) {
if(inputValue == undefined) inputValue = '';
var capitalized = inputValue.toUpperCase();
if(capitalized !== inputValue) {
modelCtrl.$setViewValue(capitalized);
modelCtrl.$render();
}
return capitalized;
}
modelCtrl.$parsers.push(capitalize);
capitalize(scope[attrs.ngModel]); // capitalize initial value
}
};
});
HTML like -
<input type="text" ng-model="name" capitalize>
The first Answer works fine. But I've solved this with this CSS code:
CSS:
#article-id {
text-transform: uppercase;
}