Is it possible to add angular custom validation constraints conditionally? - javascript

The ng-required attribute is helpful to make a field required based on a condition:
<input ... ng-required="object.isRequired" \>
But how can this type of use be extended into using my own custom validation directives? For example, say I have a custom validation fieldsMatch. Now I want to add this validation to the input field based on the property "object.needsToMatch". I would hope there would be something like:
<input ... fieldsMatch="object.needsToMatch"
If the solution is to create another directive that would handle the condition, then how can I replace it inline (inside the input element)?
Secondary question: Then how to get this working when the custom validation takes an attribute, such as in the case of fieldsMatch validator, the model's field that this field needs to match with.

I would create a directive to handle this that is restricted to an attribute.
<input ... ng-model="inputModel" fieldMatchValidator fieldToMatch="object.needsToMatch" />
and in the directive:
...directive('field-match-validator', function(){
require: 'ngModel',
scope:{
fieldToMatch: '='
},
link: function(scope, element, attrs, ngModelCtrl){
//watch on the model and validate it against scope.fieldToMatch
}
});
Obviously this is somewhat crude, but should get you started.

Related

How can I pass a controller's scope property to a custom directive?

Basic Setup:
I have a modal form that has a custom directive (child element) inside of it. The custom directive is a button. The modal consists of an input checkbox as well.
The End Goal:
Whenever the checkbox is 'checked', the custom directive/button element should be enabled. If the checkbox is 'unchecked', the custom directive/button should be disabled.
What I have so far AKA my thought process:
In the 'modalController' I put an ng-model on the checkbox input to dynamically change the value of a variable (isChecked). When the input is checked it sets the $scope.isChecked value to true, when it's unchecked, $scope.isChecked is false.
In order to disable the button I would pass the value of 'isChecked' from the modalController to the custom directive where its value can be put in the ng-checked expression on the button located inside the directive template (see-below).
The Problem
When I try this solution, the console log shows an error saying "inputCheck is not defined". This happens as soon as the page loads, so the console log gets printed before the user can even click the checkbox. Any ideas on how to make this work?
Modal html:
<div ng-controller= "modalController">
<input type="checkbox" ng-model="isChecked">
<button-directive inputCheck="isChecked"></button-directive>
</div>
ButtonDirective.js:
angular.module('starter').directive('buttonDirective', function ($uibModal) {
return {
restrict: "EA",
templateUrl: "app/directives/button-directive.html",
scope: {
inputCheck: "#"
},
controller: ['$scope', function ($scope) {
console.log(inputCheck);
}]
};
});
button-directive.html:
<button ng-checked="inputCheck">
You are doing it wrong. $scope variable is different from that directive scope declaration.
Every child, including directives, are within $scope scope, get it?
So your directive declaration don't need that scope, remove it.
angular.module('starter').directive('buttonDirective', function ($uibModal) {
return {
restrict: "EA",
templateUrl: "app/directives/button-directive.html"
};
});
And your modal, no need to pass your inputCheck attribute.
<div ng-controller= "modalController">
<input type="checkbox" ng-model="isChecked">
<button-directive></button-directive>
</div>
Then your directive html becomes
<button ng-checked="isChecked">
See this
https://plnkr.co/edit/icaufi3LJxTnbTOkmxdb
Working the way you did.
You were passing attribute wrong, angularjs have a bad thing IMO that is when you are passing values to a directive like that inputCheck, you must write it different with hiphen so it needs to be input-check
<button-directive input-check="isChecked"></button-directive>
And your scope declaration must use equal sign
scope: {
inputCheck: "="
},
https://plnkr.co/edit/5jlHaE0fvhoDPi2h8XGq?p=preview
You should do following changes:
<button-directive input-check="isChecked"></button-directive>
scope: {
inputCheck: "="
}
<button ng-disabled="inputCheck">

Custom validation as a function in Angular

In Angular, I am trying to validate a value of a field on blur. I have a list of customers, and I want to check if the model value in the field is in my list of customers. If not, I want to set the validity to false.
I know that ng-model-options="{updateOn: 'blur'} exists, however, I can't use this because the field is a typeahead so it must update based on the model. The validation is what needs to happen on blur.
The answer seems to be:
Write it as a function in the controller and use $setValidity just as you would in a directive. Use ng-blur to trigger the function in the input field.
-However, I keep running into examples where a custom validation (make the field invalid if the model value does not match one in the list) is only written as a directive. Are there examples of custom validation written as a function?
Write a directive that only triggers on blur.
However, I can't find examples that do either of these things.
Does anybody have an example of custom validation as a function OR a directive that only updates on blur of the field?
I found this link very helpful for custom validation but I still have the same problem with the difference between a function and a directive: How to add custom validation to an AngularJS form?
** My typeahead works, I do not need help getting uib-typeahead working but rather the accompanying custom validation
Here the example of custom validation triggered on blur:
angular.module('myApp', [])
.directive('customValidation', function() {
return {
require: 'ngModel',
link: function(scope, el, attrs, ngModel) {
el.bind('blur', function(event) {
scope.$apply(function() {
ngModel.$setValidity('customValidation', scope.name == 'test');
});
})
}
};
});
Plunk

AngularJS - Using $setValidity with ng-repeat directive

I am having a bit of a problem (mostly because of my lack of knowledge of AngularJS directives). I am using the Rating Directive directive from the UI Bootstrap library combined with my own custom validation. Multiple rating directive instances are created using ng-repeat from an object array from my controller. I use other scope variables to set the default "Rate Me" text, the custom CSS classes 'ratingOptions.ratingStates' and the max value limitiations 'ratingOptions.max'. All is working as desired... This is my directive code in the view (please note that the container form is called "categoryRatingFrom":
<div data-ng-repeat="cats in categories">
<div data-ng-form name="RatingFrom">
<div class="row no-bottom-padding">
<label class="col-sm-4 control-label">#{{ cats.name }}</label>
<div class="col-sm-8">
<div class="no-outline"
data-rating
data-ng-model="cats.value"
data-max="ratingOptions.max"
data-rating-states="ratingOptions.ratingStates"
data-on-hover="cats.onHover(value)"
data-on-leave="cats.onLeave()"
data-rating-validate > <!-- Notice the custom directive -->
</div>
#{{ cats.hoverState || cats.items[cats.value - 1].name || rateMe }}
</div>
</div>
</div>
</div>
Now I wish to set the directive instance to invalid should a value not being set, the default value is zero but the user must enter a value from 1 to ratingOptions.max (which is currently 6). I have a custom directive to validate this called ratingValidate. This is being invoked/bootstrapped and I am able to determine the current value of each directive instance, however I wish to initially set the directive/form-item to invalid and once the user selects a value we set the directive /form-item/instance to valid. This should be fairly easy but with using an ng-repeat I am unsure how to reference the specific form item. If anyone could help explain what I need to do whilst I experiment and search the AngularJS docs I would be most appreciative. This is my directive...
angular.module('myApp')
.directive('ratingValidate', function () {
// ratingValidate
return {
restrict: 'A', // only activate on element attribute
require: '?ngModel', // get a hold of NgModelController
link: function (scope, element, attrs, ngModel) {
// do nothing if no ng-model
if (!ngModel) {
return;
}
// Listen for change events to enable binding
element.bind('click change', function () {
console.log(element[0], attrs);
if(attrs.ariaValuenow === 0){
ngModel.$setValidity('', false); // What goes here??? How do I reference the item?
} else {
ngModel.$setValidity('', false); // What goes here??? How do I reference the item?
}
});
}
};
})
;
How Angular Validation Works
Angular uses the 'name' attribute to create the $scope variables used for validation.
For example, if you have an input field with a 'required' attribute:
<form name="myform">
<input name="firstname" ng-model="firstname" type="text" required/>
</form>
Then to access the validation properties on $scope, you would do:
var validationFailed = $scope.myform.firstname.$error.required;
Where $error is an object that has 'required' as a Boolean property.
In the 'required' directive, you would see something like this:
if(attrs.value == ''){
ngModel.$setValidity('required', true); // failed validation
} else {
ngModel.$setValidity('required', false); // passed validation
}
You can pass any string to $setValidity, and it will set the $error property for you. For example, if you did:
$setValidity('test', true)
Then there would be an $error property named 'test' and it would be set to true. You can then access the property like this:
$scope.myform.firstname.$error.test
Other validation properties that are available are:
$scope.myform.firstname.$valid
$scope.myform.firstname.$invalid
$scope.myform.firstname.$pristine
$scope.myform.$valid
$scope.myform.$invalid
$scope.myform.$pristine
Hope this helps to answer your question.
To overcome this I added the following to the directive HTML:
name="rating#{{$index}}
and in the directive
ngModel.$setValidity(attrs.name, true); // or false depending on the condition

Directive restricting input is not working with type=number

I have a custom directive that restricts input to specific symbols, in my case, only numbers. It is made after filters on ng-model in an input and it works fine in text-type inpit.
Directive declared as
.directive('onlydigitinput', function () {
return {
require: 'ngModel',
link: function (scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
var transformedInput = inputValue.toLowerCase().replace(/ /g, '').replace(/[^0-9]+/g, '');
if (transformedInput != inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
};
})
but in case of numerical input I want to use input type=number, not type=text:
<input type="number"
onlydigitinput="onlydigitinput" required="required" min="1" max="99" maxlength="2">
and then it's a problem: I have correct validation based on min-max attribute, but I still can enter all symbols, such as letters, and directive does not work as it should. Additionally, maxlength seems not to have effect. How I can possibly fix my directive to work with type="number" inputs?
There are two issues here. First, the maxlength attribute doesn't work with input type number. See e.g.
maxlength ignored for input type=“number” in Chrome.
Second, when the input type is number, your directive's function only gets a value for the inputValue parameter when it's a number. See this fiddle as an example (the console output might make it clearer).
Edit:
How crucial is it for you to use the number type input? If the spinner controls that browsers add to the input field for it aren't a necessity, I'd probably go for a custom type like described here: How to create a custom input type?
I've used something very similar for e.g. money input fields (with localized decimal separators etc.).

AngularJS form validation while adding dynamic elements

I have a form with AngularJS validation.
The problem is that I add input elements to this form using Jquery and AngularJS doesn't add those elements into the form's validation..
Here's an example: http://jsfiddle.net/ULEsy/
var input = $compile($('<input type="text" ng-model="textinput" required />'))($scope);
$("#someform").append(input);
In the example, even though the input field is not valid (empty - can be seen by the red border), the entire form is valid.
Any help?
#Ephi and I found a solution for this problem.
Apparently, you need to first append the element to the DOM, and only then use $compile. Also, the new element needs a name.
See here
angular.module("app", []).controller('someController', function($scope, $compile) {
$scope.add = function() {
var input = $('<input type="text" ng-model="textinput" name="x" required />');
$("#someform4").append(input);
$compile(input)($scope);
}
});

Categories

Resources