I'm building an app in angular js.
I want to create a restriction an input field to accept only 3, 6, 9, 12, 15 and so on.
It should mark it as no valid if the user writes for example an 8.
I couldn't find how to do this, help would be appreciated.
You can create a directive and add your own validator that checks for multiples of 3.
See plunker
Markup:
<input name="numberField" type="number" data-ng-model="model.number" data-multiple-validator="3"/>
JS:
app.directive('multipleValidator', [function(){
return {
restrict: 'AC',
require: 'ngModel',
link: function(scope, elem, attr, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
var multiple = parseInt(attr.multipleValidator);
if (!multiple) {
return;
}
ngModelCtrl.$validators.multipleValidator = function(modelValue, viewValue) {
return !!modelValue && modelValue%multiple === 0;
}
}
}
}])
If it's only the numbers 3, 6, 9, 12 and 15 then you can just use ng-pattern like so
<input name="numberField" type="number" data-ng-model="model.number" data-ng-pattern="/^(3|6|9|12|15)$/"/>
EDIT
If your multiple value is dynamic then you will have to watch for changes in your directive using the very handy $observe and re-validate when it changes. See updated plunker.
Markup:
<input placeholder="input" name="numberField" type="number" data-ng-model="model.number" data-multiple-validator="{{model.multiple}}"/>
JS:
app.directive('multipleValidator', [function(){
return {
restrict: 'AC',
require: 'ngModel',
link: function(scope, elem, attr, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
ngModelCtrl.$validators.multipleValidator = function(modelValue, viewValue) {
var multiple = parseInt(attr.multipleValidator);
if (!multiple) {
// If there is no multiple then assume that it is valid
return true;
}
return !!modelValue && modelValue%multiple === 0;
}
// Watch for changes to the multipleValidator attribute
attr.$observe('multipleValidator', function(newVal, oldVal) {
// Re-validate the input when a change is detected
ngModelCtrl.$validate();
});
}
}
}]);
There is no need to use a custom directive. Angular has built in directive called ng-pattern which lets you match the input field to your given regular expression.
Here is how i have achieved this..
CODE...
<html ng-app="num">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
</head>
<body ng-controller="numCtrl">
<form class="digits" name="digits">
<input type="text" placeholder="digits here plz" name="nums" ng-model="nums" required ng-pattern="/^([0369]|[258][0369]*[147]|[147]([0369]|[147][0369]*[258])*[258]|[258][0369]*[258]([0369]|[147][0369]*[258])*[258]|[147]([0369]|[147][0369]*[258])*[147][0369]*[147]|[258][0369]*[258]([0369]|[147][0369]*[258])*[147][0369]*[147])*$/" />
<p class="alert" ng-show="digits.nums.$error.pattern">Multiples of three only accepted.</p>
<br>
</form>
<script>
var app = angular.module('num',[]);
app.controller('numCtrl', function($scope, $http){
$scope.digits = {};
});
</script>
</body>
</html>
See this jsfiddle for a working example https://jsfiddle.net/s7mmvxq5/5/
The approach I took was to create a directive that just adds an additional $validator to the form field. Since the validator is just a function you can do whatever check you'd like in it.
This section of the angular docs might be of interest https://docs.angularjs.org/guide/forms#custom-validation
Related
I am using the currency filter shown in the "best answer" in this thread: Apply currency filter to the input field in angularjs
The problem is, I also need the input field to be numerical only, i.e. type="number". It works perfectly with type="text", but when I try to combine the two, the input disappears when the textbox loses focus.
<div class="table-cell">
<div class="field-label table-cell">Estimated Yearly<br />Savings</div>
<input ng-model="model.yearlySavings" type="number" format="currency">
</div>
JS:
mainModule.directive('format', ['$filter', function ($filter) {
return {
require: '?ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$formatters.unshift(function (a) {
return $filter(attrs.format)(ctrl.$modelValue)
});
elem.bind('blur', function (event) {
var plainNumber = elem.val().replace(/[^\d|\-+|\.+]/g, '');
elem.val($filter(attrs.format)(plainNumber));
});
}
};
}]);
Note: I would have commented in that thread, but I don't have enough "rep" to leave a comment...
I ended up using a custom formatter from another SO thread: AngularJS - How to apply currency filter on textbox as I type?
The directive is called fcsa-number: https://github.com/FCSAmericaDev/angular-fcsa-number
It took some finesse, but I got it working like this:
<input ng-model="model.yearlySavings" name="ys" type="text"
ng-class="{ 'error': formName.ys.$invalid }" fcsa-number="{ prepend: '$', preventInvalidInput: true, maxDecimals: 2 }">
The "ng-class" bit is used to dynamically style the border to red when the element listed (in this case "yearlySavings" aka "ys") is invalid. In this example, it prepends my input with "$", it prevents invalid input from being entered at all (like regular text characters), and it sets the maximum decimals to 2. Oddly enough, it does not prevent 3+ decimals from being entered like I would expect (like the regular text characters), but it does invalidate the element with more than 2.
Create directive as below
HTML
<div ng-app="myApp">
<div ng-controller="myCtrl">
{{amount}}
<input format-to-currency amount="amount">
</div>
</div>
JS
angular.module('myApp', [])
.controller('myCtrl', function($scope) {
$scope.ampunt = 2;
})
.directive('formatToCurrency', function($filter) {
return {
scope: {
amount: '='
},
link: function(scope, el, attrs) {
el.val($filter('currency')(scope.amount));
el.bind('focus', function() {
el.val(scope.amount);
});
el.bind('input', function() {
scope.amount = el.val();
scope.$apply();
});
el.bind('blur', function() {
el.val($filter('currency')(scope.amount));
});
}
}
});
link http://jsfiddle.net/moL8ztrw/7/
I want to restrict input typed length should not be more than 10 using directive in angularjs?
i do not know how to get typed value into directive?
app.directive('findLength',function(){
return{
restrict : 'EA',
require:'ngModel',
link:function(scope,ele,attr,ctrl){
console.log(ctrl.ngModel);
}
}
});
You are looking for this. This should be useful to you
HTML
<div ng-app='app' ng-controller='mainCtrl'>
<input type='text' ng-model='test' allowed-length='10' check-length/>
<span style='color:red;' ng-if='test.length === 10'>* Maximum 10 characters allowed</span>
</div>
Notice that i have used the allowed-length attribute so that the directive checkLength can be reusable for all length values. This is why we use directive as they can be reused again and again. So the best way is to provide the limit of length to the directive rather than coding the length inside the directive.
The directive is
.directive('checkLength',function(){
return {
restrict: 'A',
scope:{
ngModel: '=',
allowedLength :'='
},
link: function (scope, element) {
element.on('keypress', function(event) {
if(scope.ngModel.length === scope.allowedLength){
event.preventDefault();
}else{
scope.ngModel = element.val();
}
});
}
}
})
This just works as expected. For your simplicity here is the attached link to JSFIDDLE
You can create a template to this directive using ng-maxlength.
Check this out: Example plnkr
You can "max-length" as attribute in your input instead of Ng-maxlength.
try this one
var app = angular.module('myApp', []);
app.directive('myDirective', function() {
return {
require: 'ngModel',
link: function(scope, element, attr, mCtrl) {
angular.element(element).on("keypress", function(e) {
if (this.value.length > 10) {
mCtrl.$setValidity('charE', false);
e.preventDefault();
} else {
mCtrl.$setValidity('charE', true);
}
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<p>Try writing in the input field:</p>
<form name="myForm">
<input name="myInput" ng-model="myInput" required my-directive>
</form>
You can use ng-maxlength="10" angular pre defined Directive.
OR You can use Following Directive :-
app.directive('findLength',function(){
return{
restrict : 'EA',
require:'ngModel',
link:function(scope,ele,attr,ctrl){
angular.element(elem).on("keypress", function(e) {
if(this.value>10)
return false;
else
return true
}
}
}
I have the following code/plunkr and for some reason the form.name.$invalid is always true (see the span just after the input box). Angular's documentation doesn't say much about how the $invalid value is set. I have a hunch it has something to do with in the javascript I have ctrl.$error.taken = true/false and just having an object on the $error object set's $invalid to true. Can someone who knows how angular works behind the scenes help me out?
I want the "Name must be alpha-numeric" error to display if the name doesn't match the regex, but I want the "Name is already taken" error to display if the name is one from the list. I also what the text "error" to display if the field is one of those two errors. All of these things are working except the word "error" is always displayed. I'm trying to follow angular's standards of using $invalid.
View:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-forms-async-validation-production</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="form-example1">
<form name="form" class="css-form" novalidate>
<div>
Username:
<input type="text" ng-model="name" name="name" username />
<span ng-show="form.name.$invalid">error</span>
<br />{{name}}<br />
<span ng-show="form.name.$error.badContent">Name must be alpha-numeric</span>
<span ng-show="form.name.$error.taken">Name is already taken!</span>
</div>
</form>
</body>
</html>
Script:
(function(angular) {
'use strict';
var app = angular.module('form-example1', []);
var NAME_REGEXP = /^([a-zA-Z0-9])*$/;
app.directive('username', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
var names = ['Jim', 'John', 'Jill', 'Jackie'];
ctrl.$validators.username = function(modelValue, viewValue) {
if (ctrl.$isEmpty(modelValue)) {
return true; // consider empty model valid
}
ctrl.$error.taken = names.indexOf(modelValue) > -1;
ctrl.$error.badContent = !NAME_REGEXP.test(modelValue);
return !ctrl.$error.taken && !ctrl.$error.badContent;
};
}
};
});
})(window.angular);
Plunkr:
https://plnkr.co/edit/LBRR14PpjAQigafOVTgQ?p=preview
Use separate directives one to validate alphanumeric, and one for the username already taken issue.
$ctrl.validators.alphaNumeric exposes $error.alphaNumeric on the FormController. You don't have to manipulate the value on $error.xxx, it is set automatically based on the validator's return value.
Please check the plunkr.
Also refer to https://code.angularjs.org/1.5.8/docs/api/ng/type/ngModel.NgModelController
Though I have not verified this, but I believe you now should have ng-invalid-alpha-numeric error class, owing to the fact that now form.$error.alphaNumeric is set.
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.model = {};
});
app.directive('validateAlphaNumeric', function () {
var REGEX = /^([a-zA-Z0-9])*$/;
return {
require: 'ngModel',
link: function (scope, element, attrs, ctrl) {
ctrl.$validators.alphaNumeric = function (modelValue, viewValue) {
if (REGEX.test(viewValue)) {
return true
}
return false;
};
}
};
});
app.directive('validateUserNotTaken', function () {
return {
require: 'ngModel',
link: function (scope, element, attrs, ctrl) {
var names = ['Jim', 'John', 'Jill', 'Jackie'];
ctrl.$validators.userNotTaken = function (modelValue, viewValue) {
if (names.indexOf(modelValue) == -1) {
return true
}
return false;
};
}
};
});
HTML
<body ng-controller="MainCtrl">
<form name="myForm">
<input type="text" validate-alpha-numeric validate-user-not-taken ng-model="model.value1">
<p ng-if="myForm.$error.alphaNumeric">Should be alphanumeric</p>
<p ng-if="myForm.$error.userNotTaken">User Exists!</p>
<p ng-if="myForm.$error.alphaNumeric || myForm.$error.userNotTaken">Error</p>
</form>
</body>
Working plunkr: https://plnkr.co/edit/MZ7DMW?p=preview
I have a simple form with a directive:
<form name="testform">
<input name="testfield" type="text" ng-model="test.name" ng-minlength="3" my-validate>
</form>
Since Angular validates the field itself when entering less than 3 characters, it sets testform.testfield.$invalid.
What I want to achieve is to add some custom validation functionality in a directive and react to this angular validation/set the validation result e.g. with the help of watchers or events like so:
angular.module('myApp.directives').directive('myValidate', [function() {
return {
link: function(scope, elem, attrs) {
elem.on('keyup', function() {
// do some custom validation
// if valid, set elem to $valid
// else set $error
// something like elem.set('$valid');
}
}
};
}]);
How can I achieve the combination of HTML5, Angular and custom form validation while using Angulars testform.testfield.$invalid, testform.testfield.$valid, testform.testfield.$error etc. logic?
By default Angular provides some directives for input validation (like required, pattern, min, max etc.). If these directives doesn't suffice, custom validation is available. To be able to do that, the directive requires the model used for validation, then it can set the validity of the element like below:
.directive('myValidate', [ function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
ctrl.$setValidity('myValidate', false);
}
};
}]);
A Plunker with such a directive which will become invalid when you enter '0' is HERE.
You can read more about custom validation on Angular forms documentation.
You can do custom validation and combine it with Angular's built in validation, you just need to require ngModel, pass ctrl as the 4th value to your link function, and use ctrl.$setValidity to indicate if your custom validation passes or fails.
The example below will be invalid if the length < 3 OR if the value is the string 'invalid':
var app = angular.module('app', []);
app.controller('controller', ['$scope',
function(scope) {
scope.test = {
name: ''
};
}
]);
app.directive('myValidate', [
function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
ctrl.$parsers.unshift(function(value) {
var valid = true;
if (value === 'invalid') {
valid = false;
}
ctrl.$setValidity(attrs.name, valid);
return valid ? value : undefined;
});
}
};
}
]);
Markup:
<body ng-app="app">
<div ng-controller="controller">
<form name="testform">
<input name="testform" type="text" ng-model="test.name" ng-minlength="3" my-validate /><br/>
</form>
<span style="color:green" ng-show="testform.testform.$valid">** valid **</span>
<span style="color:red" ng-show="testform.testform.$invalid">** invalid **</span>
</div>
<script src="script.js"></script>
</body>
Here is a plunker with this code: http://plnkr.co/edit/6XIKYvGwMSh1zpLRbbEm
The Angular documentation talks about this, though it wasn't totally obvious: https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
This blog helped me get a hold on this: http://www.benlesh.com/2012/12/angular-js-custom-validation-via.html
I'm trying to create an input mask for a EU money field using http://jquerypriceformat.com/
So far in my directive, the input shows correctly to the user with the mask applied, but I believe there's something wrong, because the POST values are being sent with weird formatting, totally different than what we see in the input field.
I include the priceformat.js
<script src="js/jquery.price_format.1.8.min.js"></script>
<input type="text" currency-input ng-model...>
And on angular:
app.directive('currencyInput', function() {
return {
require: '?ngModel',
link: function($scope, element, attrs, controller) {
element.priceFormat({
prefix: '',
centsSeparator: ',',
thousandsSeparator: '.'
});
}
};
});
My input shows the value with the mask correctly, but on POST data (called by angular) it's a different value, what am I missing?
input > 2.200,80 | post > 22,0080
Thanks
From your example I don't see that link returns something.
I would write directive something like:
.directive('format', ['$filter', function ($filter) {
return {
require: '?ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$formatters.unshift(function (a) {
return $filter(attrs.format)(ctrl.$modelValue)
});
ctrl.$parsers.unshift(function (viewValue) {
elem.priceFormat({
prefix: '',
centsSeparator: ',',
thousandsSeparator: '.'
});
return elem[0].value;
});
}
};
}]);
Demo 1 Fiddle
If you want on start fire the filter, use $formatters:
Now link is:
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
var format = {
prefix: '',
centsSeparator: ',',
thousandsSeparator: ''
};
ctrl.$parsers.unshift(function (value) {
elem.priceFormat(format);
return elem[0].value;
});
ctrl.$formatters.unshift(function (value) {
elem[0].value = ctrl.$modelValue * 100 ;
elem.priceFormat(format);
return elem[0].value;
})
}
Demo 2 Fiddle
Push a $parser to the controller, and only update the value when it doesn't match the input using $setViewValue() and $render().
app.directive('currencyInput', function() {
return {
require: '?ngModel',
link: function($scope, element, attrs, controller) {
return ctrl.$parsers.push(function(inputValue) {
...
if (result != inputValue) {
controller.$setViewValue(res);
controller.$render();
}
});
}
};
});
Here's a fiddle with the logic I used for my currency input directive: Fiddle
Late to the party, but I believe this deserves another answer! I've been using the ng-currency module. It's absolutely fantastic.
I like Dubilla's approach cause of its simplicity and elegance. I decided to add (with due credits) some features on top of it to make it quite close to real world use case.
I've using it on a github project to create some useful finance directives github.
Notable extra features:
It does some rigorous checking of the inputs to give a valid response.
It has some keyboard shortcuts to make entering large numbers quicker.
I show how to integrate it with bootstrap and ngmodel css updates.
As a bonus I outputed the form's ngmonel as JSON to help people see how the form validation works in real time
It also uses a POJO as the ngmodel:
function Money() {
this.notional = 0;
this.maxValue = 99999999999.9;
this.maxValueString = "99,999,999,999.9";
this.maxPrecision = 10;
}
you can use it with Bootstrap 3 like so:
<h1>Currency Formatting directive</h1>
<div class="row">
<div class="col-md-6">
<form name="myForm">
<div class="form-group" ng-class="{'has-error': myForm.notional.$invalid && myForm.notional.$touched}">
<input type="text" ng-model="myForm.money.notional " money="money" money-input size="30" required
name="notional"
class="form-control"
placeholder="Enter notional amount"/>
<p class="help-block error" ng-show="myForm.notional.$error.required && myForm.notional.$touched">Required</p>
</div>
<button ng-disabled="myForm.$invalid" type="submit">SAVE</button>
</form>
<h2>Tips</h2>
<ol>
<li> Entering 'k' will multiply the amount by one thousand</li>
<li> Entering 'm' will multiply the amount by one million</li>
<li> Entering 'b' will multiply the amount by one billion</li>
</ol>
</div>
</div>
<p>Form debugger</p>
<pre>
form = {{ myForm | json }}
</pre>
Here's a way to handle this without jQuery using only an Angular directive. This example doesn't support decimals. It's easy to modify it to support that though, just change the $filter in the toView() function.
In my opinion this is a better approach to solve the same problem, as you can avoid loading in jQuery and the currency plugin mentioned by the author. Locale support for Euro should be supported by the use the $locale properties, but I've only tested this for use in USD.
(function() {
var app = angular.module('currencyMask', []);
// Controller
app.controller('ctrl', ['$scope', function($scope) {
$scope.amount = 100000;
}]);
// Directive
app.directive('inputCurrency', ['$locale', '$filter', function($locale, $filter) {
// For input validation
var isValid = function(val) {
return angular.isNumber(val) && !isNaN(val);
};
// Helper for creating RegExp's
var toRegExp = function(val) {
var escaped = val.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
return new RegExp(escaped, 'g');
};
// Saved to your $scope/model
var toModel = function(val) {
// Locale currency support
var decimal = toRegExp($locale.NUMBER_FORMATS.DECIMAL_SEP);
var group = toRegExp($locale.NUMBER_FORMATS.GROUP_SEP);
var currency = toRegExp($locale.NUMBER_FORMATS.CURRENCY_SYM);
// Strip currency related characters from string
val = val.replace(decimal, '').replace(group, '').replace(currency, '').trim();
return parseInt(val, 10);
};
// Displayed in the input to users
var toView = function(val) {
return $filter('currency')(val, '$', 0);
};
// Link to DOM
var link = function($scope, $element, $attrs, $ngModel) {
$ngModel.$formatters.push(toView);
$ngModel.$parsers.push(toModel);
$ngModel.$validators.currency = isValid;
$element.on('keyup', function() {
$ngModel.$viewValue = toView($ngModel.$modelValue);
$ngModel.$render();
});
};
return {
restrict: 'A',
require: 'ngModel',
link: link
};
}]);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
<div ng-app="currencyMask" ng-controller="ctrl">
<input input-currency ng-model="amount">
<p><strong>Amount:</strong> {{ amount }}</p>
</div>