Angularjs preventing validation of input fields inside dynamically hidden div - javascript

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.

Related

Can't apply limit on the input field of type=number

Below is my input field of type="number". I have applied the min and the max attributes to restrict the user to enter the value of the field. The problem is this given validation is still allowing the user beyond the limit while he types the numerical value. Please tell me how can I stop him/her entering the value above the limit.
<input name="capacity" [(ngModel)]="nService.capacity" type="number"
class="input-minimal" min="1" max="9999" placeholder="Capacity" required>
Setting attributes max and min on an input with type = "number" will prevent the user from incrementing/decrementing the value with the spinner. But it will not prevent the keyboard input. To do so, you would have to write a custom validator.
In my example I binded to the keyup event with an optional error validation block, just in case you prefer to leave the user to correct herself.
Plunker
HTML:
<input autofocus id="myModel" (keyup)="preventInput($event)"
name="myModel"
#myModel="ngModel" style="width:60px"
[(ngModel)]="capacity" type="number"
class="input-minimal" min="1" max="99"
placeholder="Capacity" required>
Typescript:
preventInput(event){
let value=this.capacity;
if (value >= 100){
event.preventDefault()
this.capacity = parseInt(value.toString().substring(0,2));
}
}
P.S. for the validation part I used the solution from here
You have to check the validity before sending it.
Here is how, with Reactive Forms :
HTML
<form novalidate (onSubmit)="submitForm()" [formGroup]="myForm">
<input type="number" formControlName="myNumber">
<button type="submit">Send</button>
</form>
TS
import {FormBuilder, FormGroup, Validators} from '#angular/forms';
myForm: FormGroup;
constructor(prviate fb: FormBuilder) {
this.myForm = fb.group({
myNumber: [0, [Validators.max(9999), Validators.min(1)]]
});
}
submitForm() {
let value = this.myForm.value;
if (this.myForm.valid) {
// Here, do your stuff when the form is valid
}
}
This is code for custom directive which I restricted as attribute:
AngularJs Custom Directive for max length:
app.directive('inputMaxlength', function() {
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
var maxlength = Number(attrs.inputMaxlength);
function fromUser(text) {
if (text.length > maxlength) {
var transformedInput = text.substring(0, maxlength);
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
return transformedInput;
}
return text;
}
ngModelCtrl.$parsers.push(fromUser);
}
};
})
Use this custom directive in HTML like
<input type="text" input-maxlength=20 ......>
And for angular form validation lots of samples are there.

Setting an input's tabIndex dynamically

I have a form in my Angular app where users can enter in multiples of the same field values using an add button.
<md-content layout-padding="">
<div>
<form name="userForm">
<div layout="row" ng-repeat="person in persons">
<md-input-container flex>
<label>First name</label>
<input ng-model="person.firstName">
</md-input-container>
<md-input-container flex>
<label>Last Name</label>
<input ng-model="person.lastName">
</md-input-container>
</div>
<md-button class="md-raised" ng-click="addAnother()">Add Another</md-button>
</form>
</div>
</md-content>
Working Codepen
So already a couple of inputs are populated, clicking the button adds a new object in order for a new set of inputs to appear.
What I would like is for the first input on the newly added row to receive focus so the user can start entering in right away.
Is there some way to set the tabIndex programatically?
Check this out. The great answer on your question you can find here: Input autofocus attribute
angular.module('ng').directive('ngFocus', function($timeout) {
return {
link: function ( scope, element, attrs ) {
scope.$watch( attrs.ngFocus, function ( val ) {
if ( angular.isDefined( val ) && val ) {
$timeout( function () { element[0].focus(); } );
}
}, true);
element.bind('blur', function () {
if ( angular.isDefined( attrs.ngFocusLost ) ) {
scope.$apply( attrs.ngFocusLost );
}
});
}
};
});
<input type="text" ng-focus="isFocused" ng-focus-lost="loseFocus()">
I have noticed you make use of a class named md-input-focused
Method 1(the best)
This is the most simplest & elegant solution to your problem. Making use of $last in angular. Add a class condition on your <md-input-container> First name Codepen like so:
<md-input-container flex ng-class="{'md-input-focused': $last}">
<label>First name</label>
<input ng-model="person.firstName">
</md-input-container>
This method requires no additional javascript changes, but will only add the focus & not make the input active. See below method if you want to make the input active.
Method 2
Check the codepen. This is the same, but the javascript way of adding the same class dynamically
FYI: JQuery needed for the below statement. Hope you have that.
This is your magic statement. Codepen
$('.layout-row:last-of-type').children().first().find('input').focus();
Add this onto your $scope.addAnother function inside a timeout(very important).
Your whole function should look like
$scope.addAnother = function() {
$scope.persons.push({});
setTimeout(function(){
$('.layout-row:last-of-type').children().first().find('input').focus();
}, 500);
}
You could even make use of the angular $timeout instead of the window setTimeout
Here you go - CodePen
Markup
<input ng-model="person.firstName" auto-focus="true">
Directive
// Inspired by Mark Rajcok's answer - http://stackoverflow.com/a/14837021/782358
.directive('autoFocus', function($timeout) {
return {
scope: { trigger: '#autoFocus' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if (value.toString() === "true") {
$timeout(function() {
element[0].focus();
});
}
});
}
};
});

How can I add an element within the directive?

I have an input box that I am trying to add functionality to. I want to write a class directive to easily add an ability to any input box. I want that when the user enters an '#', a box will pop up showing the options, using uib-typeahead. I cant figure out a way to display the box without making the original text box disappear, although ideally the box will pop up where the user is currently typing
<div class="medium-2">
<label>Age:</label>
</div>
<div class="medium-10">
<input type="text" class="form-control pop-up-variables" name="age" error-message="ageErrorMessage" required>
</input>
</div>
The block I want to be able to add is this:
<input class="variable-dropdown" type="text" uib-typeahead="number as number for number in numbers | filter:$viewValue" ng-model="selectedAttribute" placeholder="Choose variable">
</input>
I don't want to return it as a template in the directive because I dont want to replace the block that the directive is on, but I don't know how to add it correctly to the DOM.
The simple JS looks like:
app.directive('popUpVariables', function() {
return {
restrict: 'C',
controller: function($scope) {
$scope.numbers = ['One', 'Two', 'Three', 'Four', 'Five'];
},
link: function (scope, element, attrs, ngModelCtrl) {
element.on('keypress', function(event) {
if (event.which === 64) {
// This is where I want to show the second input
}
});
}
}
})

Angular form validation - hide errors on field update

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;
};

Angularjs form validation order

I have a simple html form containing regular text input. ng-minlength, ng-maxlength and ng-pattern angular built-in form input directives are set on the input.
Problem: ng-pattern check is applied before the length check by ng-minlength and ng-maxlength.
Question: how can I change the default check order: i.e. first check for the length, then apply pattern check?
Example:
<body ng-app>
<div>
<form name="myForm">
Name: <input name="name" type="text" ng-model="name" ng-minlength="3" ng-maxlength="16" ng-pattern="/^\w+$/"/>
<div ng-show="myForm.name.$dirty && myForm.name.$invalid">
<span ng-show="myForm.name.$error.pattern">Pattern error</span>
<span ng-show="myForm.name.$error.minlength || myForm.name.$error.maxlength">Length error</span>
</div>
<br/>
<input type="submit" value="Submit">
</form>
</div>
</body>
Current behavior:
enter "#" - see "Pattern error"
enter "###" - see "Pattern error"
Desired behavior:
enter "#" - see "Length error"
enter "###" - see "Pattern error"
FYI, related jsfiddle.
Thanks in advance.
Write your own directive:
var mod = angular.module("myApp", []);
mod.directive("nameValidation", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, element, attrs, ngModelCtrl) {
var validate = function (value) {
var minLen = parseInt(attrs.myMinlength, 10),
maxLen = parseInt(attrs.myMaxlength, 10),
pattern = attrs.myPattern,
match = pattern.match(/^\/(.*)\/([gim]*)$/),
lenErr = false;
if (match) {
pattern = new RegExp(match[1], match[2]);
}
if (!ngModelCtrl.$isEmpty(value)) {
ngModelCtrl.$setValidity("pattern", true);
if ((minLen && value.length < minLen) || (maxLen && value.length > maxLen)) {
ngModelCtrl.$setValidity("length", false);
lenErr = true;
}
else {
ngModelCtrl.$setValidity("length", true);
lenErr = false;
}
if (!lenErr) {
if (match && !pattern.test(value)) {
ngModelCtrl.$setValidity("pattern", false);
}
else {
ngModelCtrl.$setValidity("pattern", true);
}
}
}
else {
ngModelCtrl.$setValidity("length", true);
ngModelCtrl.$setValidity("pattern", true);
}
}
ngModelCtrl.$parsers.push(validate);
ngModelCtrl.$formatters.push(validate);
}
}
});
Then in your HTML, include the app and use the directive:
<body ng-app="myApp">
<div>
<form name="myForm">
Name: <input name="name" type="text" ng-model="name" name-validation="" my-minlength="3" my-maxlength="16" my-pattern="/^\w+$/"/>
<div ng-show="myForm.name.$dirty && myForm.name.$invalid">
<span ng-show="myForm.name.$error.pattern">Pattern error</span>
<span ng-show="myForm.name.$error.length">Length error</span>
</div>
<br/>
<input type="submit" value="Submit">
</form>
</div>
</body>
The directive uses my-minlength, my-maxlength, and my-pattern for the three values. If length fails, that will trip first. If not, then pattern will show as error if it doesn't match. Consider renaming this directive if you want to use it other places besides name as minlength, maxlength, and pattern can be passed to it via attributes. If they are left off, they will be ignored.
See jsfiddle: http://jsfiddle.net/4zpxk/6/
I searched in angular code why this behavior. Then in the function 'textInputType' that it's the specific function that handles text inputs for the angular 'input' directive I found this at the end of this function, where we can see three blocks of code.
// pattern validator
if (pattern){
//validator logic
}
// min length validator
if (attr.ngMinlength) {
//validator logic
}
// max length validator
if (attr.ngMaxlength) {
//validator logic
}
So, no matter if you change the declaration order of your ng-* attributes in the html input element you will always get same result but if you change the order of the blocks, I mean, put the min length validator block before pattern validator block you will have the result that you expect.
This is a solution for your problem but you have to make a litte change in angular code and I don't know if you really like this. But you got a very common situation where order of the declaration of validation concepts matters, so, something more must be done to handle this. Thanks
You cannot change the default check order unfortunately.
One solution is to write a custom validator, not that difficult. Based on this answer, I came up with this code (fiddle)
Usage: There is an array of validation functions in the scope, they get passed to our custom directive "validators" as:
<input name="name" type="text" ng-model="name" validators="nameValidators"/>
A validator function would look like (e.g. for the minlength constraint):
function minlength(value, ngModel) {
if( value == null || value == "" || value.length >= 3 ) {
ngModel.$setValidity('minlength', true);
return value;
}
else {
ngModel.$setValidity('minlength', false);
return;
}
}
Important points are: it takes the value and the ngModel as arguments, performs the test (here value.length >= 3) and calls ngModel.$setValidity() as appropriate.
The directive registers the given functions with ngModel.$parsers:
app.directive("validators", function($parse) {
return {
restrict: "A",
require: "ngModel",
link: function(scope, el, attrs, ngModel) {
var getter = $parse(attrs.validators),
validators = getter(scope),
i;
for( i=0; i < validators.length; i++ ) {
ngModel.$parsers.push((function(index) {
return function(value) {
return validators[index](value, ngModel);
};
})(i));
}
}
};
});
Many details can be tweaked and improved, but the outline works (again link to fiddle). Now the order of validation is explicitly set by the order of the validator functions in the nameValidators array.
If you use ng-messages you should be able to set the order via the order of ng-message elements, e.g:
<div ng-messages="field.$error">
<ul class="validation-errors">
<li ng-message="required">This has the highest prio</li>
<li ng-message="min">Second in command</li>
<li ng-message="max">I'm last</li>
</ul>
</div>
Also the docs on this: https://docs.angularjs.org/api/ngMessages/directive/ngMessages
i just changed the order of your directives, pattern first
<input name="name" type="text" ng-model="name" ng-pattern="/^\w+$/" ng-minlength="3" ng-maxlength="16"/>
EDIT: uuum, tested your fiddel without changes and it shows your desired behavior ...
directives are compiled by priority, bbut i don't know how to set angulars directives priority ... sorry, should have tested this first

Categories