5

i am using currency pipe in blur event. But it works fine for the first time, when i get an error message of validation, if i delete few numbers and come out, it wont be formatted to the currency format remains in the format what user have deleted. For example i gave this number: 36543265 so when i came out of the input it is formatted to $36,543,265.00, with the validation error message. So if i delete 265.00 from $36,543,265.00 , i still have the error validation message. so the validation error message went off and stayed with this format $36,543 but it didn't come to the proper format. How do i make it to proper currency pipe format of USD, after clearing the validation message.

How can i make it to be formatted properly based on user deletion of values.

TS:

public transformAmount(element, name) {
  if (name == "amount") {
    let formattedAmount = this.currencyPipe.transform(this.eoInfoForm.value.amount, 'USD');
    element.target.value = formattedAmount;
    this.eoInfoForm.get('amount').setValue(formattedAmount);
    if (this.eoInfoForm.get('amount').status === 'VALID') {
      this.amt = false; 
    } 
    else {
      this.amt = true;
    } 
  } 
}

HTML:

 <input type="text" class="form-control" placeholder="Amount in dolars"
                    formControlName="amount" autocomplete="off" maxlength="8" allowNumberOnly (blur)="transformAmount($event,'amount')" [ngClass]="{ 'is-invalid': amt  && eoInfo.amount.invalid }">
                    <div *ngIf="amt && eoInfo.amount.invalid" class="invalid-feedback">
                        <div *ngIf="amt && eoInfo.amount.invalid">Maximum of 8 characters including $ is allowed</div>
                      </div>

DEMO: DEMO

Mobeen Sarwar
  • 514
  • 5
  • 23
Bhrungarajni
  • 2,415
  • 11
  • 44
  • 88
  • My guess is this only works once as as soon as the $ is inserted, the code can no longer re-convert the input.. – MikeOne Mar 07 '20 at 19:08
  • Thanks for response.Is there anyother way to make this requirement work? – Bhrungarajni Mar 07 '20 at 19:09
  • @Bhrungarajni I gave you 2 options did it help you? if not let me know so I could improve my answer – Oded BD Mar 07 '20 at 22:52
  • @Bhrungarajni it's really unclear from your question what you're trying to achieve. 1) Modifying the text box is horrible UX. 2) You cannot rely on `maxlength` is you are going to format the value - you need a custom validator. Can you **clearly** re-state the rules of validation in your question? 3) Why does the text box need to be formatted? That is for user input, that should be validated as a pure value. – Kurt Hamilton Mar 18 '20 at 18:29

4 Answers4

2

you can write a somewhat simple directive to do this:

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

@Directive({ selector: "[currencyInput]" })
export class CurrencyInputDirective implements OnInit {

  // build the regex based on max pre decimal digits allowed
  private regexString(max?: number) {
    const maxStr = max ? `{0,${max}}` : `+`;
    return `^(\\d${maxStr}(\\.\\d{0,2})?|\\.\\d{0,2})$`
  }
  private digitRegex: RegExp;
  private setRegex(maxDigits?: number) {
    this.digitRegex = new RegExp(this.regexString(maxDigits), 'g')
  }
  @Input()
  set maxDigits(maxDigits: number) {
    this.setRegex(maxDigits);
  } 

  private el: HTMLInputElement;

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

  ngOnInit() {
    this.el.value = this.currencyPipe.transform(this.el.value, 'USD');
  }

  @HostListener("focus", ["$event.target.value"])
  onFocus(value) {
    // on focus remove currency formatting
    this.el.value = value.replace(/[^0-9.]+/g, '')
  }

  @HostListener("blur", ["$event.target.value"])
  onBlur(value) {
    // on blur, add currency formatting
    this.el.value = this.currencyPipe.transform(value, 'USD');
  }

  // variable to store last valid input
  private lastValid = '';
  @HostListener('input', ['$event'])
  onInput(event) {
    // on input, run regex to only allow certain characters and format
    const cleanValue = (event.target.value.match(this.digitRegex) || []).join('')
    if (cleanValue || !event.target.value)
      this.lastValid = cleanValue
    this.el.value = cleanValue || this.lastValid
  }
}

the directive converts the value to a number on focus and back to a formatted currency string on blur.

use like:

<input type="text" class="form-control" placeholder="Amount in dolars" autocomplete="off"
  formControlName="amount" currencyInput maxDigits="9" 
  [ngClass]="{ 'is-invalid': eoInfo.amount.dirty  && eoInfo.amount.invalid }">

blitz: https://stackblitz.com/edit/angular-csyslb?file=src%2Fapp%2Fcurrency-input.directive.ts

bryan60
  • 28,215
  • 4
  • 48
  • 65
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/210041/discussion-on-answer-by-bryan60-how-to-get-the-amount-converted-to-currency-pipe). – Samuel Liew Mar 21 '20 at 01:05
  • Thanks a ton, it really helped me and save my weeks together work. Thanks a lot. – Bhrungarajni Mar 21 '20 at 18:59
  • any idea about this question? https://stackoverflow.com/questions/60771823/how-to-bring-tab-focus-on-popup-as-soon-as-it-opens-rather-than-keeping-focus-on – Bhrungarajni Mar 21 '20 at 19:05
  • i have no idea of how to search to make that work, i also dont know what to be searched for that question, i tried in all ways based on my knowledge but failed everywhre – Bhrungarajni Mar 21 '20 at 19:06
  • Hi bryan60, i need a small help in this issue – Bhrungarajni Mar 27 '20 at 07:25
  • when i type some number and do tabout, when i again come back to tab, by using shiftTab, the cursor is in the last digit, can i make that to get everything selected? – Bhrungarajni Mar 27 '20 at 07:28
  • @bryan60 please help https://stackoverflow.com/questions/72400051/how-to-allow-negative-value-also-for-2-decimal-limit-using-angular13/72400466?noredirect=1#comment127901106_72400466 – Bhrungarajni May 27 '22 at 07:37
0

You have an error with your code after you insert the $ sign enter image description here

You can use this regex to take only numbers from the text

var thenum = thestring.replace( /^\D+/g, ''); // replace all leading non-digits with nothing

or just check that the first char and if it's $ remove it

Community
  • 1
  • 1
Oded BD
  • 2,788
  • 27
  • 30
  • Thanks for response, where should I add that pattern, can you please edit in my demo?? – Bhrungarajni Mar 08 '20 at 01:50
  • I tried with your both approaches and is there in my demo, could you please correct me?? – Bhrungarajni Mar 08 '20 at 16:00
  • @Bhrungarajni, no offense, but "Canonical answer" in this case will mean something similar to: "Hey, rewrite my app from scratch and fix all broken angular guidelines":) Very hot-fix similar to suggestions above can be found here: https://stackblitz.com/edit/angular-j6l2tn?file=src/app/app.component.t – malonowa Mar 18 '20 at 17:38
  • Thanks for the response, but here it is not allowing me to edited the values, for example i typed 123.56 it came as $123.56, now i want to edit something so i went in middle and trying to remove 2 and give 7, i am able to remove 2 but not able to add 7 – Bhrungarajni Mar 18 '20 at 17:52
0

I've changed some things in your code in order to make it work: Working solution

Here is the main part:

public transformAmount(name) {
    let formattedAmount = this.currencyPipe.transform(
        this.eoInfoForm.value.amount.replace(/[^\d.E+]/gm, ""),
        "USD"
    );
    this.eoInfoForm.get(name).setValue(formattedAmount);
}

What I did is invert the pipe transformation on the value before passing it again to the pipe. I used the /[^\d.E+]/gm regular expression to remove everything that is not included in :

  • Number
  • Dot character
  • "E" character
  • "+" character

The "E" and "+" are to handle a specific case when the number is very large and is displayed like $1.00E+37.

I supposed that the max length of 8 is applicable when the text has been formatted, so I didn't touch it (otherwise you need a custom validator). I just moved it to the TypeScript file since you are using reactive forms so it's better not mixing template forms concepts.

Guerric P
  • 30,447
  • 6
  • 48
  • 86
0

First thing I want to point out:

You use maxlength="8" which is fine until you start using the currency pipe. The characters added by your currency pipe will be taken into account. I don't know if that's a wanted behavior, but it definitely isn't intuitive from a user perspective.

"I see a limit of 8 characters, I can input up to 8 characters" - is the first thing I'd think when reading the validation message. I don't want to have to think of the currency formatting myself while I input my (at the time) non-formatted number.

I'd say either make the maxLength 14 (8 + 1 dollar sign + 2 commas + 1 dot + double digits is the max number possible for USD = 14 chars) and leave the validation message as value as 8. Or if you really only want 8 chars after formatting, make the validation message value as 3 (3 + 1 dollar sign + 1 commas + 1 dot + double digits = 7) having 4 digits goes to 9 chars after formatting because the extra digit + a comma are added, going to 9 chars which is more than the maxLength 8.

You can also use a custom validation for the length and properly check yourself (which is good if currency format can vary) instead of relying on the generic *ngIf="amt && eoInfo.amount.invalid"


Now for your actual issue, a solution I think would be nice is to remove the formatting while editing. It's never really practical for a user to edit and have to navigate through . , and $ when all they care when editing are the numbers really. You can make a "reverse currency pipe" when the user focuses the field so all they see are the number.

I made a quick fork of your Stackblitz demo with the assumption that the max length refers to the number of digits before the decimal point.

Let me know if anything needs changing and if that helps solve your issue at all.

Jojofoulk
  • 2,127
  • 1
  • 7
  • 25
  • Thanks for response, your demo seems to be good, but i am not able to do ctrlz/ctrlA fr selecting all the characters, should i add anything for this? – Bhrungarajni Mar 20 '20 at 04:43
  • I believe that's because of your `two-digit-decima-number.directive`. It will prevent the ctrl key to work altogether. Try working on your directive a bit, maybe by letting events with `event.ctrlKey` true go through instead of `preventDefault()` them. Or you can rework your directive to work diferently, up to you – Jojofoulk Mar 20 '20 at 05:58
  • thanks for response, i will try working on it and get back to you, and here sometimes i am failing to edit the typed amount, i am able to delete but not able replace it, after 2 or 3 times of trying, it allows me to edit. – Bhrungarajni Mar 20 '20 at 06:09
  • other than that directive can anything else be used? i choose for directive because i had in many screens the input field for currency. – Bhrungarajni Mar 20 '20 at 06:11
  • Hi i had tried upto this without using directive, but here i want to limit the alphabets and special characters other than decimal'.' could you please help me to solve this https://stackblitz.com/edit/angular-vvcpqc?file=src/app/app.component.ts – Bhrungarajni Mar 20 '20 at 10:59