Ensure that only numbers with a maximum of two decimal places are accepted in Angular 2 for input

On my webpage, there are several input boxes where users can enter numbers. I need to prevent them from entering more than two decimal places.

Although I tried using the html 5 input Step="0.00", it didn't work as expected.

I am open to any TypeScript solutions as well.

Answer №1

To see a demonstration of the directive below, click on this Plnkr link.

You can implement this functionality using the custom directive provided:

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

@Directive({
  selector: '[OnlyNumber]'
})
export class OnlyNumber {
  elemRef: ElementRef

  constructor(private el: ElementRef) {
    this.elemRef = el
  }

  @Input() OnlyNumber: boolean;
  @Input() DecimalPlaces: string;
  @Input() minValue: string;
  @Input() maxValue: string;

  @HostListener('keydown', ['$event']) onKeyDown(event) {
    let e = <KeyboardEvent> event;
    if (this.OnlyNumber) {
      // Keycodes allowed for numbers and specific actions
      if ([46, 8, 9, 27, 13, 110, 190].indexOf(e.keyCode) !== -1 ||
        (e.keyCode == 65 && e.ctrlKey === true) || 
        (e.keyCode == 67 && e.ctrlKey === true) || 
        (e.keyCode == 88 && e.ctrlKey === true) || 
        (e.keyCode >= 35 && e.keyCode <= 39)) {
          return;
        }
        if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
            e.preventDefault();
        }
      }
  }

  @HostListener('keypress', ['$event']) onKeyPress(event) {
    let e = <any> event
    let valInFloat: number = parseFloat(e.target.value)
    
    if(this.minValue.length) {
      if( valInFloat < parseFloat(this.minValue) || (isNaN(valInFloat) && e.key === "0") ) {
        e.preventDefault();
      }
    }

    if(this.maxValue.length) {
      if(valInFloat > parseFloat(this.maxValue)) {
        e.preventDefault();
      }
    }

    if (this.DecimalPlaces) {
      let currentCursorPos: number = -1;
      
      let dotLength: number = e.target.value.replace(/[^\.]/g, '').length
      
      let decimalLength = e.target.value.split(".")[1] ? e.target.value.split(".")[1].length : 0;

      if( dotLength > 1 || (dotLength === 1 && e.key === ".") || (decimalLength > (parseInt(this.DecimalPlaces) - 1) && 
        currentCursorPos > e.target.value.indexOf(".")) && ["Backspace", "ArrowLeft", "ArrowRight"].indexOf(e.key) === -1 ) {
        e.preventDefault();
      }
    }  
  }
}

The usage of HTML with this directive is demonstrated as follows:

<input type="text" OnlyNumber="true" DecimalPlaces="2" minValue="1.00" maxValue="999999999.00">

If you encounter any issues or bugs, please feel free to provide feedback in the comments section.

P.S: This directive builds upon improvements made to this answer for validating decimal points effectively.

Answer №2

I successfully implemented a solution with the help of @pipe

import { Directive, Input, Inject, HostListener, ElementRef, OnInit } from "@angular/core";

const PADDING = "000000";

@Pipe({ name: "CurrencyPipe" })
export class CurrencyPipe implements PipeTransform {
  transform(value: any, args: string[]): any {
     var clean = value.replace(/[^-0-9\.]/g, '');
    var negativeCheck = clean.split('-');
    var decimalCheck = clean.split('.');
    
     if (negativeCheck[1] != undefined) {
                        negativeCheck[1] = negativeCheck[1].slice(0, negativeCheck[1].length);
                        clean = negativeCheck[0] + '-' + negativeCheck[1];
                        if (negativeCheck[0].length > 0) {
                            clean = negativeCheck[0];
                        }

                    }
        if (decimalCheck[1] != undefined) {
                        decimalCheck[1] = decimalCheck[1].slice(0, 2);
                        clean = decimalCheck[0] + '.' + decimalCheck[1];
                    }

    return clean;
  }

  parse(value: string, fractionSize: number = 2): string {

     var clean = value.replace(/[^-0-9\.]/g, '');
    var negativeCheck = clean.split('-');
    var decimalCheck = clean.split('.');
    
     if (negativeCheck[1] != undefined) {
                        negativeCheck[1] = negativeCheck[1].slice(0, negativeCheck[1].length);
                        clean = negativeCheck[0] + '-' + negativeCheck[1];
                        if (negativeCheck[0].length > 0) {
                            clean = negativeCheck[0];
                        }

                    }
        if (decimalCheck[1] != undefined) {
                        decimalCheck[1] = decimalCheck[1].slice(0, 2);
                        clean = decimalCheck[0] + '.' + decimalCheck[1];
                    }

    return clean;
  }

}

Additionally, I utilized this Pipe Extension in my directive.

import { Directive, Input, Inject, HostListener, OnChanges, ElementRef, Renderer, AfterViewInit, OnInit } from "@angular/core";
import { CurrencyPipe } from '../../shared/pipe/orderby';

@Directive({ selector: "[CurrencyFormatter]" })
export class CurrencyFormatterDirective {

  private el: HTMLInputElement;

  constructor(
    private elementRef: ElementRef,
    private currencyPipe: CurrencyPipe
  ) {
    this.el = this.elementRef.nativeElement;
  }

  ngOnInit() {
    this.el.value = this.currencyPipe.parse(this.el.value);
  }

  @HostListener("focus", ["$event.target.value"])
  onFocus(value) {
    this.el.value = this.currencyPipe.parse(value); // opposite of transform
  }

  @HostListener("blur", ["$event.target.value"])
  onBlur(value) {
    this.el.value = this.currencyPipe.parse(value);
  }

  @HostListener("keyup", ["$event.target.value"]) 
  onKeyUp(value) {
    this.el.value = this.currencyPipe.parse(value);
  }

}

Remember to import the Directive in your component

import { CurrencyFormatterDirective } from '../../shared/directive/showOnRowHover';
import { CurrencyPipe } from '../../shared/pipe/orderby';
providers: [CurrencyPipe,
          CurrencyFormatterDirective]

Lastly, add the Directive to your html Input

<input type="text"  [(ngModel)]="invoiceDetail.InvoiceAmount" class="form-control" placeholder="Enter invoice amount"
          CurrencyFormatter>

Answer №3

<input type='number' step='0.01' value='0.00' placeholder='0.00' />

Avoid using Step, instead use step. If necessary for compatibility with older browsers, consider implementing a JavaScript alternative.

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

Apply a CSS class when the tab key is pressed by the user

Currently in my Angular 14 project, I am working on a feature where I need to apply "display: block" to an element once the user reaches it using the tab key. However, I am struggling with removing the "display: block" when the user tabs out of the element ...

Executing Functions from Imported Modules in Typescript

Is there a way to dynamically call a method from my imported functions without hard-coding each function name in a switch statement? I'm looking for a solution like the following code: import * as mathFn from './formula/math'; export functi ...

What are some ways to conceal methods within a class so that they are not accessible outside of the constructor

I am a newcomer to classes and I have written the following code: class BoardTypeResponse { created_on: string; name: string; threads: string[]; updated_on: string; _id: string; delete_password: string; loading: BoardLoadingType; error: Bo ...

Angular 11 reactive form not reflecting real-time changes in input field

I'm currently working on an Angular project and I need to dynamically update a reactive form field with data retrieved from an API called getNextCode(). I have a function that calls the API service like this: ngOnInit(): void { this.NextCodeService.g ...

Initiate a standalone test within VS Code specifically for an Angular service

I have a task with its task.spec. The task is a standard procedure, not specific to any particular platform, except for the fact that it is accessed via Dependency Injection. I want to run the tests of the task.spec directly from VS Code, without needing ...

Changing Image to Different File Type Using Angular

In my Angular Typescript project, I am currently working on modifying a method that uploads an image using the input element of type file. However, I no longer have an input element and instead have the image file stored in the assets folder of the project ...

Ways to manage multiple Observables

Two Observables are being returned from different services, each providing only one value (similar to Observable.just()). In TypeScript, types play a crucial role in this context. Is there a method to determine when both Observables have been resolved (in ...

Can the automatic casting feature of TypeScript be turned off when dealing with fields that have identical names?

Imagine you have a class defined as follows: Class Flower { public readonly color: string; public readonly type: string; constructor(color: string, type: string) { this.color = color; this.type = type; } Now, let's introduce anoth ...

What is the best way to ensure that consecutive if blocks are executed in sequence?

I need to run two if blocks consecutively in TypeScript, with the second block depending on a flag set by the first block. The code below illustrates my scenario: export class Component { condition1: boolean; constructor(private confirmationServic ...

Enhance your app with the seamless navigation experience using Ionic 2

As a beginner in Angular2, Ionic2, NodeJS ... I am experimenting with writing some code to learn. In my journey, I attempted to create a screen with 3 tabs and a menuToggle. When the application is launched, clicking the menuToggle button on the first tab ...

Retrieving the return type of generic functions stored in a map

In this unique scenario, I have a dictionary with polymorphic functions that accept the same argument but return different results: const dict = { one: { foo<A>(a: A) { return [a] as const } }, two: { foo<A>(a: A) { ...

NgFor in IONIC4 can only bind to Iterables like Arrays

Hello everyone, I am facing an issue in my code: it is displaying the error: (ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.) I am trying ...

The ngx-translate/core is throwing an error message that says: "HttpClient provider not found!"

I recently downloaded the ngx-translate/core package and have been diligently following the provided documentation. However, I'm facing an issue with getting the translations to work. Here are the steps I've taken: 1] Definition in the AppModul ...

Enhance the Error class in Typescript

I have been attempting to create a custom error using my "CustomError" class to be displayed in the console instead of the generic "Error", without any success: class CustomError extends Error { constructor(message: string) { super(`Lorem "${me ...

Is there a method for verifying the application signature in Ionic?

For the past 2 days, I've been on a quest to find information about app certificate validation libraries/functions in Ionic. After discovering SignatureCheck.java for Android (link: enter link description here), I wonder if there is a similar solution ...

Where can I locate htmlWebpackPlugin.options.title in a Vue CLI 3 project or how can I configure it?

After creating my webpage using vue cli 3, I decided to add a title. Upon examining the public/index.html file, I discovered the code snippet <title><%= htmlWebpackPlugin.options.title %></title>. Can you guide me on how to change and cu ...

How can you create a function in typescript that only allows parameters of a specific type?

Here's what I'm trying to accomplish: function validateNumber(param: ???): param is number { } If the parameter can be a number, such as number | string or number | boolean, it should be accepted by the function. However, if the type is somethin ...

Reduce the use of if statements

Is there a way to optimize this function by reducing the number of if statements? The currentFeatures are determined by a slider in another file. The cost is updated if the currentFeatures do not match the previousFeatures, and if the user changes it back ...

I possess a table that showcases MatIcon buttons. Upon clicking on a button, two additional buttons should appear at the bottom of the table

I am working on a table that contains mat-icon-buttons. When the button is clicked, it should display 2 additional buttons at the end of the table. Upon clicking the first button, its color changes from primary to red, and I would like to add two more butt ...

What could be causing Typescript Compile Errors to occur during runtime?

In the Visual Studio React + Redux template project, I have created a react component with the following "render()" method: public render() { return ( <React.Fragment> <h1>Welcome to the Adventure Company {th ...