Field that only permits numerical input without triggering events for other characters

I've encountered some issues with the default behavior of the HTML number input and I'm looking to create a simple input that only allows numbers.

To address this, I have developed a directive as shown below:

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

@Directive({
  selector: 'input[numbersOnly]'
})
export class NumberDirective {

  constructor(private _el: ElementRef) { }

  @HostListener('input', ['$event']) onInputChange(event) {
    const initalValue = this._el.nativeElement.value;
    this._el.nativeElement.value = initalValue.replace(/[^0-9]*/g, '');
    if (initalValue !== this._el.nativeElement.value) {
      event.stopPropagation();
    }
  }

}

Despite using "event.stopPropagation()", I find that the event still propagates even when the input value remains unchanged from the user's perspective.

How can I prevent event propagation when the field value hasn't changed?

Edit: To provide better clarity, here is a StackBlitz example: https://stackblitz.com/edit/angular-numbers-only-directive

Answer №1

It doesn't seem possible to prevent this event using a directive with HostListener.

Angular seems to detect the event before your listener is called, so stopping propagation may not have an effect on Angular events like ngModelChange that you react to in the parent component. (This is unconfirmed, so I could be mistaken)

My suggestion would be to create your own event to indicate a value change and emit it only when you want it to be emitted:

export class NumberDirective {
  @Output() numberChanged = new EventEmitter<number>();

  constructor(private _el: ElementRef) { }

  @HostListener('input', ['$event']) onInputChange(event) {
    const initialValue = this._el.nativeElement.value;
    this._el.nativeElement.value = initialValue.replace(/[^0-9]*/g, '');
    if (initialValue === this._el.nativeElement.value) {
      this.numberChanged.emit(initialValue);
    }
  }

}

Then, replace the binding of ngModelChanged with numberChanged:

<input type="text" [ngModel]="value" (numberChanged)="onChange($event)" numbersOnly/>

Demo: https://stackblitz.com/edit/angular-numbers-only-directive-a2mv6e

Answer №2

To utilize in reactive forms, I developed a unique custom component that implements ControlValueAccessor:

Here is a snippet of the component declaration:

@Component({
  selector: 'number',
  template: `
    <input type="text" [(ngModel)]="value" numbersOnly/>
  `,
  ...
  ]
})
export class NumberInputComponent implements ControlValueAccessor {
  ...

  set value(value) {
    const tmp = value.replace(/[^0-9]*/g, '');
    if(tmp && tmp !== '' && +tmp !== +this._value) {
      this._value = +tmp;
      this.onChange(this._value);
    }
  }

  ...
}

Usage Example:

<number [formControl]="formControl"></number>

Check out the stackblitz demo here.

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

Differences between useFormState and useForm in Next.js version 14

Currently, I am intrigued by the comparison between using the react hook useForm and the react-dom useFormState. The Nextjs documentation suggests utilizing useFormState, but in practice, many developers opt for the react hook useForm. I am grappling with ...

Navigating from a Card to a new View in Angular

I am currently developing a project using Angular (latest version). Within my application, I have the functionality to dynamically generate bootstrap cards from an Order Array and display them in my "Order-Item-Component through its respective template. ...

What is the method for extracting individual elements from an object returned by a React hook in Typescript?

I am currently working on a component that needs access to the query parameters from React Router. To achieve this, I have been using the use-react-router hook package in my project. This is what I am trying to accomplish: import React from "react; impor ...

Having difficulty grasping the significance of the data received from the API response

Currently, as I am working on my personal Portfolio for a Web Developer course, I have encountered an issue with correctly implementing my API to retrieve information from the database. Previously, I faced no problem when using a .json file, but now, I am ...

Invoke the method saved as a class attribute

Within my codebase, there exists a class named Process. This class has a constructor that takes in a type of object () => void. Initially, everything seemed to be working perfectly fine when I passed this object into the class. However, issues arose whe ...

What is the most effective way to determine the data type of a variable?

My search skills may have failed me in finding the answer to this question, so any guidance towards relevant documentation would be appreciated! I am currently working on enabling strict type checking in an existing TypeScript project. One issue I'v ...

Vertical and horizontal tabs not functioning properly in Mat tabs

I successfully created a vertical material tab with the code below, but now I am looking to incorporate a horizontal tab inside the vertical tab. Attempting to do so using the code provided results in both tabs being displayed vertically. Below is my code ...

Unable to loop through the "dataList" retrieved from a service call to the java backend within an Angular 9 application

After receiving JSON data from a Java backend service called houseguidelines, the information is sent to an Angular application via a service call. I am attempting to iterate over this returned JSON data and add it to an array I have created. Unfortunately ...

The TypeScript fs/promises API is experiencing compilation issues when used in JavaScript

While working with TypeScript and the fs/promises API, I encountered an error when compiling and running the TypeScript code. The error message displayed: internal/modules/cjs/loader.js:968 throw err; ^ Error: Cannot find module 'fs/promises' ...

Angular displays X items in each row and column

I've been struggling with this task for the past 2 hours. My goal is to display a set of buttons on the screen, but I'm facing some challenges. The current layout of the buttons doesn't look quite right as they appear cluttered and unevenly ...

declare wrong TypeScript type in external library

I am currently using winston 3.0 in combination with the @types/winston types. Unfortunately, it seems that these types are not completely compatible, leading to an error in the types that I am unsure how to rectify. Below is the code snippet in question: ...

What is the process for clearing a selection from a table?

I have been facing this issue for some time now. I am working with a basic table where selecting a row highlights it. However, I want to enhance my button functionality by adding a "Remove Selection" feature. When clicked, I need the selected row to lose i ...

Utilizing Angular for handling multiple subscriptions

Hey, I'm trying to figure out how to "doSomething" with the accurate final value of total. Can someone help me with this code snippet? let total: number = 0; for (let articleId in items) { this.articleService.getArticleById(articleId).subscribe((ar ...

Error in TypeScript in VSCode when using the React.forwardRef function in a functional component

We are developing our component library using JavaScript instead of TypeScript. In our project's jsconfig.json file, we have set checkJs: true. All components in our library are functional and not based on class components. Whenever a component needs ...

The conditional expression is failing to function properly when applied to multiple form controls in the latest version of @rxweb/[email protected]

I am currently working on a form that includes various field validations based on a boolean variable called isNewCustomer. this.customerDetailsFormGroup = this._formBuilder.group({ customer: [this.order.customer_details.name, [RxwebValidators.r ...

Angular is intercepting HTTP subscriptions without finalizing them

Within my code, I have a system in place where an interceptor is triggered to check for HTTP responses with the status code 401. If this code is detected, it initiates a request for a refresh-token by calling the method refreshToken(), before retrying the ...

What is the reason behind my styled component only displaying the final state in its className logs?

Here is my implementation using styled components with the version "@types/styled-components": "^5.1.26" and I'll provide you with an example of my code. // index.tsx import React, { useEffect, useState } from 'react'; i ...

``If you're looking to integrate a 360-degree product viewer into your Angular application, here is a

I am in need of showcasing a 360 Product viewer consisting of an array of 36 car images in base64 format. Despite attempting to utilize the angular-three-sixty Package by @mediaman, I was only met with an empty canvas. Does anyone have experience in implem ...

What is the process for including a dependency in an npm package in order to install another npm package?

As I work on creating an npm package for my Angular app, I find myself in need of including a dependency that already exists on npm. My dependency object will include the following entries: "dependencies": { "@angular/animations": "^6.1.0", "@angu ...

When hosting on render.com, the session data is not retained when redirecting to other routes

My login API checks if the user has a saved cookie in MongoDB and saves the value into req.session using the req.session.save() method. Afterward, it redirects to another route to create a response and send the client session data to be used. This function ...