Validation of the html input control in angular for the swiss license plates
21. 7. 2022
We were having requirement to validate html input in the mobile selfcare app for the swiss car license plates. The requirement was to limit the input only for the allowed characters. First two characters are alphabetical and next one up to 6 are digits. Typical pattern is BE123456. The mobile app is running on angular framework. First thought was using one of the many angular input mask validation libraries such as angular-mask, syncfusion, ngx-currency etc. We may have in the future more specific requirements to enter only 2 alphabetical characters from existing cities etc , we decided not to use library and to implement in the angular the validation our selfs.
The requirement included for every position allow only correct characters , e.g. for the 2 first positions only alphabetical and next only digit. The submit icon should be active only when it is a valid swiss license plate e.g. first two characters alphabetical and at least one number.
The custom validation consist of using regular expression which allows with the or operator entering first, second alphabet and then digist.
public regexPlateInput = '^[a-zA-Z]$|^[a-zA-Z][a-zA-Z]$|^[a-zA-Z][a-zA-Z][0-9]$|^[a-zA-Z][a-zA-Z][0-9][0-9]$|^[a-zA-Z][a-zA-Z][0-9][0-9][0-9]$|^[a-zA-Z][a-zA-Z][0-9][0-9][0-9][0-9]$|^[a-zA-Z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]$|^[a-zA-Z][a-zA-Z][0-9][0-9][0-9][0-9][0-9][0-9]$';
Second regular expression is used to make submit icon visible after the text contains valid swiss car license plate.
public regexPlateSubmit = '^[a-zA-Z][a-zA-Z][0-9]+$';
The input control was enhanced with events :
- keypress – to capture and validate the input characters
- keydown – to validate backspace and delete, which can violate the format for the license plate
- keyup.enter – to capture enter for validating the submit of the text
- angular placeholder was entered to give the user an example
<div id="input-box">
<!-- [@slide]="slide" (@slide.done)="slideDone($event)" -->
<label for="input-text"></label>
<input id="input-text" #entryText autocomplete="off" [(ngModel)]="chatItemDef.value"
(keyup.enter)="onEnter($event)" (blur)="onFocusChanged($event.type)" (focus)="onFocusChanged($event.type)"
[placeholder]="isPlate ? placeholder : ''" (keypress)="validateKeyPress($event)" (keydown)="validateKeyDownPress($event)" (paste)="onPaste($event)" />
<img id="img-send" src="assets/images/Send.svg" alt="" (click)="onSubmit()" *ngIf="showSubmit">
</div>
ds
The script for the control is quite complex, but we have it under control and can in the future react to the requirements.
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
import {ChatService} from '../chat.service';
import {ChatItemDef, InputPattern} from '../../../core/session.service';
@Component({
selector: 'ph-chat-entry-text',
templateUrl: 'chat-entry-text.component.html',
styleUrls: ['./chat-entry-text.component.scss']
})
export class ChatEntryTextComponent implements OnInit {
@ViewChild('entryText', {static: true}) entryInput: ElementRef;
@Input() chatItemDef: ChatItemDef;
@Output() focusChanged = new EventEmitter<string>();
public showSubmit=true;
public placeholder='';
public isPlate=false;
public regexPlateInput = '^[a-zA-Z]$|^[a-zA-Z][a-zA-Z]$|^[a-zA-Z][a-zA-Z][0-9]$|^[a-zA-Z][a-zA-Z][0-9][0-9]$|^[a-zA-Z][a-zA-Z][0-9][0-9][0-9]$|^[a-zA-Z][a-zA-Z][0-9][0-9][0-9][0-9]$|^[a-zA-Z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]$|^[a-zA-Z][a-zA-Z][0-9][0-9][0-9][0-9][0-9][0-9]$';
public regexPlateSubmit = '^[a-zA-Z][a-zA-Z][0-9]+$';
constructor(private chatSvc: ChatService) {
}
onEnter(event) {
if (this.isPlate) {
if (this.onSubmitPlateValidation (event.target.value)) {
this.onSubmit();
}
else {
event.preventDefault();
return false;
}
} else this.onSubmit();
}
onSubmitPlateValidation(str) {
let regexSubmit = new RegExp(this.regexPlateSubmit);
if (regexSubmit.test(str)) {
return true;
} else {
return false;
}
}
onSubmit() {
if (this.chatItemDef.value) { // value must exist
let value: string = this.chatItemDef.value;
if (this.chatItemDef.pattern === InputPattern.NUMBERPLATE) {
value = value.toUpperCase().replace(/\s/g, '');
}
value = value.trim();
this.chatSvc.processAnswer(value, value);
this.chatItemDef.value = '';
this.chatSvc.currentInputType = null;
}
}
onFocusChanged(type: string) {
this.focusChanged.emit(type);
}
ngOnInit(): void {
if (!this.chatSvc.isApple()) {
const endPos = this.chatItemDef.value ? this.chatItemDef.value.length : 0;
this.entryInput.nativeElement.setSelectionRange(endPos, endPos); // set caret
this.entryInput.nativeElement.focus();
}
this.chatItemDef.value = '';
if (this.chatItemDef.pattern === InputPattern.NUMBERPLATE) {
this.showSubmit=false;
this.isPlate=true;
this.placeholder='BE12345';
}
}
validateKeyDownPress (event) {
if (this.isPlate) {
if (event.key === 'Backspace') {
// Execute your logic here.
let str = event.target.value;
let pos =event.target.selectionStart;
str = str.substring(0,pos-1)+str.substring(pos);
if (this.validateMaskPlate(str)) {
return true;
} else {
event.preventDefault();
return false;
}
} else if (event.key === 'Delete' ) {
let str = event.target.value;
let pos =event.target.selectionStart;
str = str.substring(0,pos)+str.substring(pos+1);
if (this.validateMaskPlate(str)) {
return true;
} else {
event.preventDefault();
return false;
}
}
}
}
validateKeyPress(event) {
if (this.isPlate) {
let str = '';
str = event.target.value;
let pos = event.target.selectionStart;
str = str.substring(0,pos)+String.fromCharCode(!event.charCode ? '' : event.charCode)+str.substring(pos);
if (this.validateMaskPlate(str)) {
return true;
} else {
event.preventDefault();
return false;
}
}
return true;
}
validateMaskPlate (str) {
//alert(e.target.selectionStart);
let regex = new RegExp(this.regexPlateInput);
let regexSubmit = new RegExp(this.regexPlateSubmit);
if (regex.test(str)||str.length ===0) {
if (regexSubmit.test(str)&& str.length>0) {
this.showSubmit = true;
} else {
this.showSubmit = false;
}
return true;
} else {
return false;
}
}
onPaste(e) {
e.preventDefault();
return false;
}
}
Back to Blog