Angular Directive Regex Alphanumeric Validation - regex

I'm kinda new to Angular and Regex and I'm currently stuck with a problem. I need to create an Angular Directive that allows an input field to do the following:
Alphanumeric only
First input must be a letter
One space only between words
No special character
I've done numbers 1-3 my problem comes in #4. The input still accepts '_' , '`', '&', 'ˆ' and some other special characters. I just need to negate all special characters.
Here is my directive:
export class AlphaNumericFieldDirective{
private regex: RegExp = new RegExp(/^[a-zA-Z]([a-zA-Z0-9]+ ?)*$/);
private specialKeys: Array<string> = ['Backspace', 'Space', 'Tab', 'End', 'Home'];
constructor(private el: ElementRef) {}
#HostListener('keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
if (this.specialKeys.indexOf(event.key) !== -1) {
return;
}
let current: string = this.el.nativeElement.value;
let next: string = current.concat(event.key);
if (next && !String(next).match(this.regex)) {
event.preventDefault();
}
}
}
And here is the sample output featuring some of the characters I want to negate
I would appreciate the help, thanks

This solution is working for me.
import { Directive, HostListener } from "#angular/core";
#Directive({
selector: "[appAlphabetOnly]",
})
export class AlphabetOnlyDirective {
#HostListener("keydown", ["$event"]) public onKeydown(event: KeyboardEvent) {
if (
(event.keyCode >= 15 && event.keyCode <= 64) ||
event.keyCode >= 123 ||
(event.keyCode >= 96 && event.keyCode <= 105)
) {
event.preventDefault();
}
}
}

Related

Need angular 2+ 'number only' directive that uses type as number, regex and allows copy paste

I have an input type number. I have a directive that successfully blocks characters other than numbers. And this also allows only single decimal point.
What I need is, the directive should allow copy from field and paste into the field as well.
<input type="number" appNumbersOnly />
import { Directive, ElementRef, HostListener } from "#angular/core";
#Directive({
selector: '[appNumbersOnly]'
})
export class NumbersOnlyDirective {
public text;
private regex: RegExp = new RegExp(/^-?[0-9]+(\.[0-9]*){0,1}$/g);
private specialKeys: Array<string> = ['Backspace', 'Tab'];
constructor(private el: ElementRef) {}
#HostListener('keypress', ['$event']) onKeyDown(event: KeyboardEvent) {
if (this.specialKeys.indexOf(event.key) !== -1) return;
const current: string = this.el.nativeElement.value;
const next: string = current.concat(event.key);
if(next.includes('.')){
if(this.text == next) event.preventDefault();
this.text= next;
}
if ((next && !String(next).match(this.regex))) {
event.preventDefault();
}
}
}
How to make this directive allow copy paste?
I am not sure about your requirenment. But I think this can help you.
copy
Angular 5 - Copy to clipboard
Paste
Angular - On Paste Event Get Content

Force user to type a regex on input

I have been trying to do a regexp to force the user to just type what I want in the input and NOT display it in the input field. I have try use many events, but the event type it on the input field and have to make an extra validation or found some errors using the code. Never found the correct event.
this is my html file:
<input [(ngModel)]="someModel" (keydown)="validate($event);">
this is my component
validateRegex(event: any) {
let regexp = new RegExp(/^([0-9][0-9]?)(\.([0-9][0-9]?)?)?$/);
let keyPress = event.key;
if(!event.ctrlKey && (event.altKey || event.shiftKey)) {
event.preventDefault();
event.stopPropagation();
}
if(!event.ctrlKey && keyPress.length < 2) { //Make sure it just press 1 digit, avoid stuff like "backspace"
if(keyPress == '0' || keyPress == '1' || keyPress == '2' || keyPress == '3' || keyPress == '4' || keyPress == '5'
|| keyPress == '6' || keyPress == '7' || keyPress == '8' || keyPress == '9' || keyPress == '.' ) {
let test = this.someModel ? this.someModel.toString() + event.key: event.key;
if (!regexp.test(test)) {
event.preventDefault();
event.stopPropagation();
}
} else {
event.preventDefault();
event.stopPropagation();
}
}
}
Now this validate the user to insert just decimals with 2 digits after and before the dot, also doesn't allow the dot to be on his own. The problem with this code, is that if the user types '99.' and then clicks/arrows backwards to the first 9, he will be able to insert a 9 at the beginning, breaking the regular expression. event just only got the key inserted, not sure where is going to be inserted.
If I don't use keydown/keypress the display input will change already, the validation will be made, but the UI will change. In keydown, the UI is still not changed. I don't want to block the user from moving around the input neither
Now yes, I could solve this problem using a mask, but the library mask I got have problems and have some errors too. Shamefully I cannot use any other.
Can this be solve with the proper event or asking for the altered value somehow on the keydown or It is possible to create my own mask and solve this?
html
<input #input [ngModel]="value" (ngModelChange)="onChange($event);">
ts
import { Component, ViewChild } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
value = '0';
#ViewChild('input', { static: false }) input;
onChange(value: string) {
let regexp = /^([0-9][0-9]?)(\.([0-9][0-9]?)?)?$/;
if (!value) {
this.value = '';
}
if (!regexp.test(value)) {
this.input.nativeElement.value = this.value
} else {
this.value = value;
}
}
}
https://stackblitz.com/edit/angular-phfkcy?file=src%2Fapp%2Fapp.component.html

How to enter only a number like this XX.XXX,XX (currency)in the input while Im typing?(Angular)

How can I type only a number in this format xx.xxx,xx(currency). If I type letters, characters and other special characters i would like to be stopped. I attached the code. I would appreciate any help. Thanks!
import { Directive, HostListener, ElementRef, Input } from '#angular/core';
#Directive({
selector: '[currencyNumeric]'
})
export class CurrencyDirective {
regexStr = 'reg exp for xx.xxx,xx';
#Input() currencyNumeric: boolean;
constructor(private el: ElementRef) { }
#HostListener('keypress', ['$event']) onKeyPress(event) {
return new RegExp(this.regexStr).test(event.key);
}
#HostListener('paste', ['$event']) blockPaste(event: KeyboardEvent) {
this.validateFields(event);
}
validateFields(event) {
setTimeout(() => {
this.el.nativeElement.value = this.el.nativeElement.value.replace(/currency reg exp for XX.XXX,XX/g, '').replace(/\s/g, '');
event.preventDefault();
}, 100)
}
}
<input type="text" currencyNumeric >
You can use this code
add this directive
import { Directive, HostListener, ElementRef, OnInit } from '#angular/core';
import {MycurrencyPipe} from './mycurrency.pipe';
#Directive({
selector: '[appMycurrency]',
providers:[MycurrencyPipe]
})
export class MycurrencyDirective implements OnInit{
private el: any;
constructor(
private elementRef:ElementRef,
private formatcurrencypipe:MycurrencyPipe
) {
this.el = this.elementRef.nativeElement;
}
ngOnInit(){
this.el.value = this.formatcurrencypipe.transform(this.el.value);
}
#HostListener("focus",["$event.target.value","$event"])
onFocus(value,event) {
this.el.value = this.formatcurrencypipe.parse(value); // opossite of transform
if(event.which== 9)
{
return false;
}
this.el.select();
}
#HostListener("blur", ["$event.target.value"])
onBlur(value) {
this.el.value = this.formatcurrencypipe.transform(value);
}
#HostListener('keydown', ['$event']) onKeyDown(event) {
let e = <KeyboardEvent> event;
if ([46, 8, 9, 27, 13, 110, 190].indexOf(e.keyCode) !== -1 ||
// Allow: Ctrl+A
(e.keyCode === 65 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+C
(e.keyCode === 67 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+V
(e.keyCode === 86 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+X
(e.keyCode === 88 && (e.ctrlKey || e.metaKey)) ||
// Allow: home, end, left, right
(e.keyCode >= 35 && e.keyCode <= 39)) {
// let it happen, don't do anything
return;
}
// Ensure that it is a number and stop the keypress
if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
e.preventDefault();
}
}
}
the directive needs this Pipe
import { Pipe, PipeTransform } from '#angular/core';
const padding = "000000";
#Pipe({
name: 'mycurrency'
})
export class MycurrencyPipe implements PipeTransform {
private prefix: string;
private decimal_separator:string;
private thousands_separator:string;
private suffix:string;
constructor(){
this.prefix='$ ';
this.suffix='';
this.decimal_separator='.';
this.thousands_separator = ',';
}
transform(value: string, fractionSize:number = 0 ): string {
if(parseFloat(value) % 1 != 0)
{
fractionSize = 2;
}
let [ integer, fraction = ""] = (parseFloat(value).toString() || "").toString().split(".");
fraction = fractionSize > 0
? this.decimal_separator + (fraction+padding).substring(0, fractionSize) : "";
integer = integer.replace(/\B(?=(\d{3})+(?!\d))/g, this.thousands_separator);
if(isNaN(parseFloat(integer)))
{
integer = "0";
}
return this.prefix + integer + fraction + this.suffix;
}
parse(value: string, fractionSize: number = 2): string {
let [ integer, fraction = "" ] = (value || "").replace(this.prefix, "")
.replace(this.suffix, "")
.split(this.decimal_separator);
integer = integer.replace(new RegExp(this.thousands_separator, "g"), "");
fraction = parseInt(fraction, 10) > 0 && fractionSize > 0
? this.decimal_separator + (fraction + padding).substring(0, fractionSize)
: "";
return integer + fraction;
}
}
and you can use it like <input appMycurrency type="text" />
don't forget to add MycurrencyPipe, MycurrencyDirective in declarations on NgModule
full app is here here
Use capturing groups (by index) in Regular Expressions. like this:
this.el.nativeElement.value = this.el.nativeElement.value.replace(/(\d{2})(\d{3})(\d{2})/, "$1.$2,$3")

Or statement in Validators.pattern Regex to match 3 out of 4 password rules for password validation in Angular

I am trying to include the following password rules via Validators.pattern in my angular 4 project using regex.
My external stakeholder would like a users password to be valid if it included a lowercase letter, uppercase letter and a Number OR Special character.
Below is the regex I am using, Pattern 1 works fine as does pattern2, however when I try to do the OR of them in pattern3 it does not work.
//passwordPattern1='^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).{8,}$';// must include numbers
//passwordPattern2='^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[#?!#$%^&*-]).{8,}$';//must include special characters
passwordPattern3='^(((?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[A-Z])(?=.*?[#?!#$%^&*-.])))';//Numbers or Special characters
Below is how I add it to my formGroup. see line 3
this.form = this.fb.group({
username: [null, Validators.compose([Validators.required, Validators.minLength(4), Validators.maxLength(20)])],
password: [null, Validators.compose([Validators.required,Validators.minLength(8), Validators.maxLength(128), Validators.pattern(this.passwordPattern3)])],
confirmPassword: [null, Validators.compose([Validators.required, Validators.minLength(8), Validators.maxLength(128)])]
}, {validator: matchingPasswords('password', 'confirmPassword')});
Does anyone know why the third password pattern does not seem to work in angular, and what I could do to make it work?
I tested the REGEX at http://rubular.com/ in passwordPattern3 and it works as desired. Anything I may be missing in Angulars validators.pattern()?
Regarding the security of these requirements- I am aware these rules are not the best approach for the security of a user. For this effort, I, unfortunately, do not have the influence to change the requirements from the external stakeholder. But I am awarewhy such rules may be ineffective
I know this answer doesn't use RegEx, but you could do this with custom validator functions:
function hasUpperCaseChar(val: string): boolean {
return val.split('')
.reduce((current, next) => current || next === next.toUpperCase(), false);
}
function hasLowerCaseChar(val: string): boolean {
return val.split('')
.reduce((current, next) => current || next === next.toLowerCase(), false);
}
function moreThanOneInteger(val: string): boolean {
return val.split('')
.reduce((current, next) => current || !isNaN(parseInt(next)), false);
}
function moreThanOneSpecialCharacter(val: string): boolean {
return val.split('')
.reduce((current, next) => current || '#?!#$%^&*-'.split('').includes(next), false);
}
// In another function file (e.g. MY_CUSTOM_VALIDATORS)
export const MY_VALIDATORS: any = {
password: {
gtOneInteger: (control: AbstractControl): boolean {
return hasUpperCaseChar(control.value) && hasLowerCaseChar(control.value) && moreThanOneInteger(control.value)
? null
: { gtOneInteger: false };
},
gtOneSpecialChar: (control: AbstractControl): boolean {
return hasUpperCaseChar(control.value) && hasLowerCaseChar(control.value) && moreThanOneSpecialCharacter(control.value)
? null
: { gtOneSpecialChar: false };
},
gtOneIntegerOrSpecialChar: (control: AbstractControl): boolean {
return hasUpperCaseChar(control.value) && hasLowerCaseChar(control.value) && moreThanOneInteger(control.value) && moreThanOneSpecialCharacter(control.value)
? null
: { gtOneIntegerOrSpecialChar: false };
}
}
};
// Updated your current functionality:
this.form = this.fb.group(
{
// The Validators.compose() function is not necessary with Angular 4+.
username: [null, [Validators.required, Validators.minLength(4), Validators.maxLength(20)]],
// Your previous validation:
// password: [null, [Validators.required,Validators.minLength(8), Validators.maxLength(128), Validators.pattern(this.passwordPattern3)]],
// New validation (you can use any of the ones defined above):
password: [null, [Validators.required, Validators.minLength(8), Validators.maxLength(128), MY_VALIDATORS.gtOneIntegerOrSpecialChar]]
confirmPassword: [null, [Validators.required, Validators.minLength(8), Validators.maxLength(128)]]
},
{
validator: matchingPasswords('password', 'confirmPassword')
}
);

Directive to allow decimal numbers with only 1 decimal point

I have an angular application where an input field should allow only positive numbers with one decimal point. In my directive I am replacing anything other than 0-9 and '.'.But currently my application is accepting multiple decimal values.
It should accept:
0.5
0.56
Not
0.5.5 or 0..5
PFB the code:
link: function (scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
// this next if is necessary for when using ng-required on your input.
// In such cases, when a letter is typed first, this parser will be called
// again, and the 2nd time, the value will be undefined
if (inputValue === undefined) {
return '';
}
var transformedInput = inputValue.replace(/[^0-9\.]/g, '');
if (transformedInput !== inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
This question might seem ignorant but I have tried all the solutions provided before this but changing my regular expression according to the same doesn't seem to work. It accepts right now multiple '.'.
Thanks in advance.
Here is fiddle http://jsfiddle.net/oora0t93/ check it:-
app.directive('inputPrice', function () {
return {
restrict: 'EA',
template: '<input name="{{inputName}}" ng-model="inputValue" />',
scope: {
inputValue: '=',
inputName: '='
},
link: function (scope) {
scope.$watch('inputValue', function(newValue,oldValue) {
if(String(newValue).indexOf(',') != -1)
scope.inputValue = String(newValue).replace(',', '.');
else {
var index_dot,
arr = String(newValue).split("");
if (arr.length === 0) return;
if (arr.length === 1 && (arr[0] == '-' || arr[0] === '.')) return;
if (arr.length === 2 && newValue === '-.') return;
if (isNaN(newValue) || ((index_dot = String(newValue).indexOf('.')) != -1 && String(newValue).length - index_dot > 3 )) {
scope.inputValue = oldValue;
}
}
});
}
};
});
I got it. I did not find a regular expression solution so did it using some javascript string manipulations. PFB the code:
var firstIndexOfDecimal = transformedInput.indexOf('.');
var lastIndexofDecimal = transformedInput.lastIndexOf(".");
if(firstIndexOfDecimal !== lastIndexofDecimal){
transformedInput = transformedInput.substr(0,lastIndexofDecimal) + transformedInput.substr(lastIndexofDecimal+1, transformedInput.length);
}
Thanks for the help.