5

I have a number of input boxes on a html page. I want to restrict the user from entering any numbers after 2 decimals.

Currently try to apply html 5 input Step="0.00" but doesn't work.

Any typescript solution is also ok

ER.SHASHI TIWARI
  • 917
  • 1
  • 10
  • 25

3 Answers3

4

See demo of below directive in Plnkr.

You can achieve this using the following directive :

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) {
      if ([46, 8, 9, 27, 13, 110, 190].indexOf(e.keyCode) !== -1 ||
        // Allow: Ctrl+A
        (e.keyCode == 65 && e.ctrlKey === true) ||
        // Allow: Ctrl+C
        (e.keyCode == 67 && e.ctrlKey === true) ||
        // Allow: Ctrl+X
        (e.keyCode == 88 && e.ctrlKey === true) ||
        // Allow: home, end, left, right
        (e.keyCode >= 35 && e.keyCode <= 39)) {
          // let it happen, don't do anything
          return;
        }
        // Ensure that it is a number and stop the keypress
        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) {
      // (isNaN(valInFloat) && e.key === "0") - When user enters value for first time valInFloat will be NaN, e.key condition is 
      // because I didn't want user to enter anything below 1.
      // NOTE: You might want to remove it if you want to accept 0
      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;    
      if (typeof this.elemRef.nativeElement.selectionStart == "number") {
          currentCursorPos = this.elemRef.nativeElement.selectionStart;
      } else {
        // Probably an old IE browser 
        console.log("This browser doesn't support selectionStart");
      }

      let dotLength: number = e.target.value.replace(/[^\.]/g, '').length
      // If user has not entered a dot(.) e.target.value.split(".")[1] will be undefined
      let decimalLength = e.target.value.split(".")[1] ? e.target.value.split(".")[1].length : 0;

      // (this.DecimalPlaces - 1) because we don't get decimalLength including currently pressed character 
      // currentCursorPos > e.target.value.indexOf(".") because we must allow user's to enter value before dot(.)
      // Checking Backspace etc.. keys because firefox doesn't pressing them while chrome does by default
      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 HTML usage is as follows:

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

If you find any bugs with it please let me know in the comments below.

P.S: This directive is improved work upon this answer to validate decimal points.

Community
  • 1
  • 1
Dhyey
  • 4,275
  • 3
  • 26
  • 33
  • On Chrome (Version 58.0.3029.110), Angular 2.4.0 - decimal places check does not work (this.elemRef.nativeElement.selectionStart is null). But really neat directive otherwise. – nbo May 12 '17 at 10:06
  • Thanks. I am using Angular 4.0.0 and chrome 57.0.2987.133 (64-bit) on OS X El Capitan. I fixed a firefox bug and it's now also working on Firefox v53. Feel free to suggest an edit if you find a workaround – Dhyey May 12 '17 at 11:41
  • @Dhyey If you type in a decimal point number and select or highlight the last digit after the decimal and try replacing it with another number, it wouldn't work. – absingharora Oct 06 '17 at 22:20
  • I wrote a similar directive in angularJS and wrote this function to get the cursor position as a cross-browser solution. It has worked for me so far: `var getCursorPosition = function (elem) { var pos = 0; if (document.selection) { elem.focus(); var sel = document.selection.createRange(); sel.moveStart('character', -elem.value.length); pos = sel.text.length; } else if (elem.selectionStart || elem.selectionStart == '0') { pos = elem.selectionStart; } return pos; };` Sorry about it looking odd in this format. Comments don't keep their formatting. – Grungondola Jun 06 '18 at 12:56
3

I got the solution using @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;
  }

}

And Pipe Extends 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); // opossite 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);
  }



}

Import Directive on your component

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

And Directive on your html Input

 <input type="text"  [(ngModel)]="invoiceDetail.InvoiceAmount" class="form-control"  placeholder="Enter invoice amount"
          CurrencyFormatter>
ER.SHASHI TIWARI
  • 917
  • 1
  • 10
  • 25
  • 1
    This works well, but I had to import PipeTransform from core, and add the pipe as a provider in the directive to get it injected, and added it to my module's declarations – user917170 May 22 '17 at 04:24
-2
<input type='number' step='0.01' value='0.00' placeholder='0.00' />

don't use Step use step and if still you want old browser's support you can opt for javascript code for same

anshuVersatile
  • 2,030
  • 1
  • 11
  • 18