Angular directive: Validate value before binding it to model - angularjs

Is there a way to not bind value to model , and ONLY bind it to model when value is valid.

Use $parsers. The example below restricts input to your model for numbers only. Obviously you can change that to be whatever you require to make your input valid.
angular.module('app').
directive('onlyDigits', function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
if (inputValue == undefined) return '';
var transformedInput = inputValue.replace(/[^0-9]/g, '');
if (transformedInput !== inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
};
});
<input type="text" name="number" only-digits>
Code sample comes from this SO question

Related

AngularJS - directive for only numbers with decimal places

I would like to create directive for writing only numbers with decimal places. I have this code:
zpc. directive('onlyNumbers', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
if (inputValue == undefined) return '';
var transformedInput = inputValue.replace(/^[0-9](,[0-9]{0,2})?/g, '');
if (transformedInput !== inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
};
});
I can write numbers with decimal places but other characters too.
Thanks for advices
angular.directive('decimalPlaces',function(){
return {
link:function(scope,ele,attrs){
ele.bind('keypress',function(e){
var newVal=$(this).val()+(e.charCode!==0?String.fromCharCode(e.charCode):'');
if($(this).val().search(/(.*)\.[0-9][0-9]/)===0 && newVal.length>$(this).val().length){
e.preventDefault();
}
});
}
};
});
<input type="number" step="0.01" ng-model='somemodel' decimal-places>
You can also used https://www.npmjs.com/package/angular-input-masks.
Hopefully this will help.
use ng-pattern directive, set regular expression.
<input type="text" ng-model="onlyNumbers" ng-pattern="/^[0-9]+\.?[0-9]*$/">

How to access ng-model from attribute directive?

I'm trying to write a custom directive to validate input value: does it belong to the specified range. The problem is that I can't access ng-model without knowing the name of the scope variable which is used for ng-model. Considering that directive has to be reused with different inputs I want to access ng-model directly. I did try to use scope[attrs.ngModel] but got the undefined value. How can read ng-model value inside directive? Thank you.
netupApp.directive('between', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
scope.$watch('dataSubmitting', function(dataSubmitting){
if (dataSubmitting) {
var min = Number(attrs.min);
var max = Number(attrs.max);
console.log(attrs.ngModel); // "number"
console.log(scope[attrs.ngModel]); // undefined
var inputText = scope.number; // that is the var used in ng-model
console.log(min); // 10
console.log(inputText); // would be the input value
console.log(max); //20
if (inputText <= min || inputText >= max) {
scope.alerts.push({
msg: 'error',
type: 'danger',
icon: 'warning',
'closable': true
});
}
}
});
}
};
});
You should hook into the Angular validation system and add your validator function to either the $validators or $asyncValidators collections (in your case I think $validators is enough, no need for async).
The validator functions receive the model value as an argument :
link: function(scope, elm, attrs, ctrl) {
var min = Number(attrs.min);
var max = Number(attrs.max);
ctrl.$validators.between = function(modelValue, viewValue) {
if (modelValue <= min || modelValue >= max) {
//do something here or just return false
return false;
}
return true;
}
}
In the view you can get the validation error messages like this :
<div ng-messages="formName.inputName.$error">
<p ng-message="between">The value is not in the required range<p>
</div>
Reference doc : https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
The proper way to get the ngModel.$viewValue is:
app.directive('between', function () {
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
ngModel.$render = function () {
var newValue = ngModel.$viewValue;
console.log(newValue)
};
}
};
});
Have a look at tutorial underneath when wanting to invoke the ngModel.$setViewVAlue from the directive
https://egghead.io/lessons/angularjs-using-ngmodel-in-custom-directives
According to angluarJs doc:$parse
you need to parse the attrs, please try:
var getter=$parse(attrs.ngModel);
var setter=getter.assign;
setter(scope,getter(scope));

angular restriction to not allow spaces in text field

I do not want a user to enter spaces in a text field. I don't want it on submit validation but rather - a space will not show up on the text field when they click it.
The selected answer is arguably not very unobtrusive. And if you need to use it in multiple places, you'll end up with duplicated code.
I prefer to prevent the input of spaces using the following directive.
app.directive('disallowSpaces', function() {
return {
restrict: 'A',
link: function($scope, $element) {
$element.bind('input', function() {
$(this).val($(this).val().replace(/ /g, ''));
});
}
};
});
This directive can be invoked like this:
<input type="text" disallow-spaces>
<input ng-model="field" ng-trim="false" ng-change="field = field.split(' ').join('')" type="text">
Update:
To improve code quality you can create custom directive instead. But don't forget that your directive should prevent input not only from keyboard, but also from pasting.
<input type="text" ng-trim="false" ng-model="myValue" restrict-field="myValue">
Here is important to add ng-trim="false" attribute to disable trimming of an input.
angular
.module('app')
.directive('restrictField', function () {
return {
restrict: 'AE',
scope: {
restrictField: '='
},
link: function (scope) {
// this will match spaces, tabs, line feeds etc
// you can change this regex as you want
var regex = /\s/g;
scope.$watch('restrictField', function (newValue, oldValue) {
if (newValue != oldValue && regex.test(newValue)) {
scope.restrictField = newValue.replace(regex, '');
}
});
}
};
});
If you want to achieve it without writing directive
ng-keydown="$event.keyCode != 32 ? $event:$event.preventDefault()"
THe directive Jason wrote did not work for me. I had to change return false to: e.preventDefault() like so:
app.directive('disallowSpaces', function() {
return {
restrict: 'A',
link: function($scope, $element) {
$element.bind('keydown', function(e) {
if (e.which === 32) {
e.preventDefault();
}
});
}
}
});
This works to prevent entering any special chars including spaces:
app.directive('noSpecialChar', function() {
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function(inputValue) {
if (inputValue == null)
return ''
let cleanInputValue = inputValue.replace(/[^\w]|_/gi, '');
if (cleanInputValue != inputValue) {
modelCtrl.$setViewValue(cleanInputValue);
modelCtrl.$render();
}
return cleanInputValue;
});
}
}
});
Use without jquery
angular.module('app').directive('disallowSpaces', function () {
return {
restrict: 'A',
require: 'ngModel',
scope: {
maxvalue: '=',
},
link: function ($scope, $element, attr, ngModelCtrl) {
$element.bind('keydown', function () {
function transformer(text) {
if (text) {
var transformedInput = text.replace(/ /g, '');
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
return transformedInput;
}
return undefined;
}
ngModelCtrl.$parsers.push(transformer);
});
},
};
});
// use disallow-spaces
<input type="text" ng-model="name" disallow-spaces />
You can achieve this without writing a directive.
<input ng-model="myModel" ng-keydown="$event.keyCode != 32 ? $event:$event.preventDefault()">
For Angular 9 ,Keycode is not support.
Below code can help you for that.
keyDownHandler(event) {
if (event.code === 'Space') {
event.preventDefault();
}
}

How to incorporate a custom directive into angular's validation framework

I have a directive that is designed to be assigned to a normal text input.
<input type="text" ng-model="fooModel" foo-input size="30"
placeholder="insert value"></input>
I have lots of validation functions for it like testing the precision of the numbers and I use a $parsers to control the value that is submitted.
myApp.directive('fooInput', function () {
return {
restrict: 'A',
require: 'ngModel',
controller: function ($scope, $element, $attrs) {
this.errorMessage = ""
},
link: function (scope, element, attrs, ctrl)
return ctrl.$parsers.push(function (inputValue) {
var originalVal = element.val();
if (!testForOverPrecision(numericVal)) {
//do something here to set the directive as invalid
}
if (originalVal != inputValue) {
ctrl.$setViewValue(res);
ctrl.$render();
}
});
I have 2 questions:
How do I get this to work with the isValid service and do I have to have a controller scope for the error message
Is it correct for me to push the $parser inside a return statement
I am using Angular 1.2x and I created a directive to determine if the text contains the # symbol.
.directive('noAt', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (/#/.test(viewValue)) {
ctrl.$setValidity('noAt', false);
return undefined;
} else {
ctrl.$setValidity('noAt', true);
return viewValue;
}
});
}
};
})

Replace comma with a dot in input field

European countries uses a comma-sign (,) instead of a dot (.) when they enter decimal numbers. So I want to replace the dot sign with a comma when users enter input. I'm aware that input=number do this but I need support for IE.
I guess directive is the best to do this? I gave it a try with the code below. But it fails.
.directive('replaceComma', function(){
return {
restrict: 'A',
replace: true,
link: function(scope, element, attrs){
scope.$watch(attrs.ngModel, function (v) {
var convert = String(v).replace(",",".");
attrs.NgModel = convert;
});
}
}
});
The convert variable is correct. But the value does not change in the input box. So I guess attrs.ngModel = convert, is wrong?
I think there is no need to do this like a directive.
say your template is
<input ng-model='someModel'>
in your controller,
$scope.$watch('someModel',function(newVal){
$scope.someModel = newVal.replace(/,/g,'.');
})
ng-model is a two-way binding, so it should work
Via directive:
.directive('replacecomma', function () {
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
scope.$watch(attrs.ngModel, function (newVal) {
if (newVal !== undefined && newVal !== null) {
ngModelCtrl.$setViewValue(String(newVal).replace(/,/g, '.'));
element.val(String(newVal).replace(/,/g, '.'));
}
})
}
}
});
var mystring = "this,is,a,test"
mystring = mystring.split(',').join(' ');
mystring contain ==> "this is a test"
In your template:
<input type="text" ng-model="someModel" replace-comma >
in your module :
.directive('replaceComma', function(){
return {
require: 'ngModel',
link: function (scope, element, attr, ngModelCtrl) {
function fromUser(text) {
if (text) {
var transformedInput = text.replace(/,/g, '.')
if (transformedInput !== text) {
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
}
return transformedInput;
}
return undefined;
}
ngModelCtrl.$parsers.push(fromUser);
}
};});

Resources