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

Having an issue with receiving an "undefined variable" error message when trying to validate a form using PHP and AJAX

Upon loading my page, an error message appears: Notice: Undefined variable: titleErr in index.php on line 141 I have already initialized the $titleErr variable at the start of my PHP code, what could be causing this issue? PHP: <?php $titleE ...

An error is thrown when attempting to use npm install, stating "integrity checksum failed. The expected sha1 checksum was sha1-6G...=, but the actual checksum was sha512

I have been browsing through various posts on different platforms trying to solve my issue, but unfortunately, I haven't had any luck. Despite having no prior experience with Angular, I was tasked with installing npm and running an unfamiliar Angular ...

Searching for several arrays in Angular

Hello everyone, I have an API that returns data like this: [ [{ "id": 1, "qte": 12, "date_creation": "2020-08-17T00:00:00+02:00", "date_update": "2020-08-17T00:00:00 ...

Executing a series of imported functions from a TypeScript module

I'm developing a program that requires importing multiple functions from a separate file and executing them all to add their return values to an expected output or data transformation. It's part of a larger project where I need these functions to ...

Using aliases in npm packages is not supported

I am working on creating an npm package that I want to use in another application. During development, I set a path in tsconfig for importing various modules instead of using a relative path. However, when I download my package into the test app, it is una ...

What are some effective ways to manage repetitive HTML elements like headers and footers in Angular 4?

Within my Angular web project, I find myself using a variety of different headers on multiple pages. Is there a way to streamline this process by coding the header and footer once and having them automatically included in one or more pages? I am looking ...

Angular 13: SyntaxError Encountered: Token 'export' Not Recognized

After upgrading Angular from version 12 to 13, I encountered an error when running the app: "Uncaught SyntaxError: Unexpected token 'export'." Here are some additional details for context: In the angular.json configuration file, I had specified ...

What is the best way to interact with and modify the relationships of "many-to-many" fields in my database table?

As someone who is new to nestjs, I am working with two entities: @Entity({ name: "Books" }) @ObjectType() export class Book { @PrimaryGeneratedColumn() @Field() id: number; @Column() @Field() title: string; @ManyToMany(() => Auth ...

How to make a div stand out when clicked in an Angular application

Within my code, there is a booking-list div that I am utilizing to showcase booking timings. When hovering over this div, the background-color changes, as depicted in the image below. https://i.stack.imgur.com/Iz1r6.png My current dilemma is that when se ...

Visual Studio Code's Intellisense is capable of detecting overloaded functions in JavaScript

What is the best way to create a JavaScript overload function that can be recognized by Visual Studio Code IntelliSense, and how can this be properly documented? A good example to reference is Jasmine's it() function shown below: function it(expecta ...

Modify the color of the select element when it is in an open state

If you're new to Material UI, I have a select element that I would like to change the color of. When 'None' is selected, I want the background color of the input field above it to match the 'None' section. Then, when the dropdown m ...

Using MatTableDataSource in a different Angular component

I am working with two components, namely Order and OrderDetail. Within the Order component, I have a MatTableDataSource that gets populated from a service. OrderComponent Prior to the constructor listData: MatTableDataSource<DocumentDetailModel>; ...

What is the best way to showcase images at random in Angular?

I am trying to display a random array of images in the UI, but I'm encountering an error with innerHTML when using the code below in TypeScript. randomPic(){ this.randomNum= Math.floor(Math.random() * this.myPix.length); console.log(this.rando ...

How can jsPDF be used with Angular2 in Typescript?

Recently, I developed an Angular2 application that is capable of generating JSON data. My main goal was to store this JSON output into a file, specifically a PDF file. This project was built using Typescript. To achieve the functionality of writing JSON d ...

What is the process for creating documentation for a TypeScript enum type with the format of { [key]: value }

I am currently developing a logger service for nodeJS using Typescript. One important component of this project is an enum that looks like this: enum LOG_TYPES { NONE = 0, ERROR = 1, WARN = 2, INFO = 3, DEBUG = 4, } Along with the enum, I have i ...

Encountering Issues with TypeScript Strict in Visual Studio Code Problems Panel

I have discovered that I can optimize my TypeScript compilation process by utilizing the --strict flag, which enhances type checking and more. Typically, I compile my TypeScript code directly from Visual Studio Code with a specific task that displays the c ...

Why is the getElement().getProperty("value") function not functioning properly?

I am facing an issue with reading a property in my web component. I am puzzled as to why it is not working correctly. I created a simple example, and after clicking the button, I expect to retrieve the value of the property, but it returns null. I am unsur ...

Angular services do not hold onto data in memory

Working on an app with a date filter and encountering an issue: I am trying to store data in my service, but when I route, the array in the service keeps resetting itself. How can I maintain the data permanently in my service? Below is the code snippet: ...

The fuse box was to blame for triggering a GET request to http://localhost:4444/, resulting in the error message

I encountered an issue with fuse-box and P5.js where I received a GET http://localhost:4444/ net::ERR_CONNECTION_REFUSED. The complete code can be accessed on GitHub. The fuse.js file contains the following configuration: const { FuseBox, WebIndexPlugin ...

Error encountered while implementing onMutate function in React Query for Optimistic Updates

export const usePostApi = () => useMutation(['key'], (data: FormData) => api.postFilesImages({ requestBody: data })); Query Definition const { mutateAsync } = usePostApi(); const {data} = await mutateAsync(formData, { onMutate: ...