The directive for angular digits only may still permit certain characters to be entered

During my exploration of implementing a digits-only directive, I came across a solution similar to my own on the internet:

import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[appOnlyDigits]'
})

export class OnlyDigitsDirective {
  // Implementation code for restricting input to only digits
}

However, I encountered an issue where typing Shift+3 twice results in entering '^' and occasionally '+' characters. How can this problem be resolved without altering the structure of the directive?

Answer №1

Check out this code snippet that I've been successfully using in my current project:

import { Directive, HostListener, Input, ElementRef } from '@angular/core';

@Directive({
  selector: '[appNumbersOnly]'
})
export class OnlyNumbersDirective {
  constructor(private el: ElementRef) { }

  @Input() allowMultiLine = false;
  @Input() allowNegative = false;
  @Input() allowDecimal = false;
  @Input() maxLength = 0;
  regex: RegExp;

  @HostListener('keypress', ['$event'])
  onKeyPress(event: KeyboardEvent) {
    this.validate(event, event.key === 'Enter' ? '\n' : event.key);
  }

  @HostListener('paste', ['$event'])
  onPaste(event: Event) {
    const pastedText = (window as any).clipboardData && (window as any).clipboardData.getData('Text') || (event as ClipboardEvent) && (event as ClipboardEvent).clipboardData.getData('text/plain');
    this.validate(event, pastedText);
  }

  @HostListener('cut', ['$event'])
  onCut(event: Event) {
    this.validate(event, '');
  }

  validate(event: Event, text: string) {
    const txtInput = this.el.nativeElement;
    const newValue = (txtInput.value.substring(0, txtInput.selectionStart)
      + text + txtInput.value.substring(txtInput.selectionEnd));
    if (!this.regex) {
      this.regex = (eval('/^'
        + (this.allowNegative ? '-?' : '')
        + (this.allowDecimal ? '((\\d+\\.?)|(\\.?))\\d*' : '\\d*')
        + '$/g') as RegExp);
    }
    const lines = this.allowMultiLine ? newValue.split('\n') : [newValue];
    for (const line of lines) {
      const lineText = line.replace('\r', '');
      if (this.maxLength && lineText.length > this.maxLength || !lineText.match(this.regex)) {
        event.preventDefault();
        return;
      }
    }
  }

}

Answer №2

Even though replicating this issue is challenging, a reliable solution is to simply replace the input value. Change the input element type to HTMLInputElement first. Then apply the replacement in one, two, or all three of the following locations:

  inputElement: HTMLInputElement;

  constructor(public el: ElementRef) {
    this.inputElement = el.nativeElement;
  }

  @HostListener('keydown', ['$event'])
  onKeyDown(e: KeyboardEvent) {
    if (
      this.navigationKeys.indexOf(e.key) > -1 || // Allow: navigation keys: backspace, delete, arrows etc.
      (e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A
      (e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C
      (e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V
      (e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X
      (e.key === 'a' && e.metaKey === true) || // Allow: Cmd+A (Mac)
      (e.key === 'c' && e.metaKey === true) || // Allow: Cmd+C (Mac)
      (e.key === 'v' && e.metaKey === true) || // Allow: Cmd+V (Mac)
      (e.key === 'x' && e.metaKey === true) // Allow: Cmd+X (Mac)
    ) {
      // Let it continue, no action needed.
      this.inputElement.value = this.inputElement.value.replace(/\D/g, '');
      return;
    }
    // Check for number and block keypress.
    if (
      (e.shiftKey || e.key < '0' || e.key > '9') &&
      (e.key < 'numpad 0' || e.key > 'numpad 9')
    ) {
      e.preventDefault();
    }
    this.inputElement.value = this.inputElement.value.replace(/\D/g, '');
  }

  @HostListener('keyup', ['$event'])
  onKeyUp(e: KeyboardEvent) {
    this.inputElement.value = this.inputElement.value.replace(/\D/g, '');
  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing an object property for @Input binding in Angular: A step-by-step guide

I am currently delving into Angular and Ionic Frameworks, honing my skills through practice. I'm encountering an issue with a basic @Input test, where I am trying to iterate through an array of Tab Pages and then display each tab using <ion-tab> ...

NgFor is designed to bind only to Iterables like Arrays

After exploring other questions related to the same error, I realized that my approach for retrieving data is unique. I am trying to fetch data from an API and display it on the page using Angular. The http request will return an array of projects. Below ...

Angular downgrades from version 13.3.8 to 13.3.7

When I input the command: ng v I am shown the version as "Angular: 13.3.8" in the image. Can someone explain where this version is coming from and how can I change it back to 13.3.7? Furthermore, I noticed that the packages listed in the screenshot are d ...

Implementing Express.js allows for the seamless casting of interfaces by the body within the request

I have created a similar structure to ASP.NET MVC and uploaded it on my github repository (express-mvc). Everything seems fine, but I am facing an issue with casting the body inside the request object to match any interface. This is what I am aiming for: ...

What is the best way to fully reload an Angular component when the route is changed?

I'm looking for a way to reload or refresh a sidebar component when the route changes. Below is the code I currently have: constructor( private auth: AuthService, private router: Router, private changeDetector: ChangeDetectorRef ) { ...

Using Angular filter pipe to customize markers in Leaflet maps

I am currently working on a select element called district, which lists all the districts in the city. My objective is to apply a filter that will dynamically display only the leaflet markers corresponding to the selected district on the map. Any suggesti ...

Exploring the functionalities of TypeScript's mapKey and pick features

I am looking to convert the JavaScript code shown below into TypeScript, but I don't want to use loadish.js. let claimNames = _.filter<string>(_.keys(decodedToken), o => o.startsWith(ns) ); let claims = <any>( _.mapKeys(_ ...

I am looking to implement tab navigation for page switching in my project, which is built with react-redux and react-router

Explore the Material-UI Tabs component here Currently, I am implementing a React application with Redux. My goal is to utilize a panelTab from Material UI in order to navigate between different React pages. Whenever a tab is clicked, <TabPanel value ...

Angular is throwing a RangeError due to exceeding the maximum call stack size

Encountering a stackOverflow error in my Angular app. (see updates at the end) The main issue lies within my component structure, which consists of two components: the equipment component with equipment information and the socket component displaying conn ...

Utilizing TypeScript's Type Inference to Simplify Function Combinations

I'm facing a challenge with what should be simple. The types aren't coming through as expected when trying to combine a couple of functions. Is there a way to have TypeScript infer the types without explicitly specifying them? import { pipe, map ...

Challenge with the scope of 'this' in Typescript

Whenever I invoke the findFromList function from a component, it triggers this particular error message: ERROR TypeError: Cannot read property 'searchInArray' of undefined at push../src/app/shared/services/filter.service.ts.FilterService ...

Ways to invoke a slice reducer from a library rather than a React component

I've been working on a React application using the React Boilerplate CRA Template as my foundation. This boilerplate utilizes Redux with slices, which is great for updating state within a React component. However, I'm facing a challenge when try ...

Angular examine phrases barring the inclusion of statuses within parentheses

I require some assistance. Essentially, there is a master list (arrList) and a selected list (selectedArr). I am comparing the 'id' and 'name' from the master list to those in the selected list, and then checking if they match to determ ...

What is causing the ESLint error when trying to use an async function that returns a Promise?

In my Next.js application, I have defined an async function with Promise return and used it as an event handler for an HTML anchor element. However, when I try to run my code, ESLint throws the following error: "Promise-returning function provided t ...

I need a way to call a function in my Typescript code that will update the total value

I am trying to update my total automatically when the quantity or price changes, but so far nothing is happening. The products in question are as follows: this.products = this.ps.getProduct(); this.form= this.fb.group({ 'total': ...

Referring to TypeScript modules

Consider this TypeScript code snippet: module animals { export class Animal { } } module display.animals { export class DisplayAnimal extends animals.Animal { } } The objective here is to create a subclass called DisplayAnimal in the dis ...

"Encountered an npm error with code EACCESS while trying to install @angular/cli

System Information: Operating System: Ubuntu 16.04 Node.js Version: v8.11.1 (installed using a package manager) NPM Version: v5.6.0 Upon attempting to install @angular/cli after a fresh npm installation, I encountered an EACCESS error related to permiss ...

What is the best way to iterate through a collection of two or more arrays in order to determine the total length of all

https://i.stack.imgur.com/PpFlB.pngI currently have multiple Arrays containing various inputs this.listNumber = [ { "GenericQuestions": [ { "input": "long", }, { "input": & ...

Exciting Update: Previously, webpack version 5 did not automatically include polyfills for node.js core modules (such as React JS, TypeScript, and JWT)!

Having trouble verifying the jwt token in React with TypeScript and encountering this error, how can I fix it? ` const [decodedToken, setDecodedToken] = useState<null | JwtPayload | string>(null); const verifyToken = (token: string) => { t ...

Error in Angular 2 component when loading background images using relative URLs from an external CSS skin

In my angular2 component, I am utilizing a third-party JavaScript library. The skin CSS of the component attempts to load images using relative URL paths. Since I am following a component-based architecture, I prefer to have all component dependencies enca ...