8

Cant seem to figure this out. I have tried many different variations. This is on an Angular project.

I want a percent number to always be shown with two decimal places even if the user only types a whole number.

I cant switch the data type around as a lot of other code has been written around it being a number.

The problem is that TypeScript does not allow var and I am having trouble getting to add the extra zeros or round said number to two decimals. It always seems to strip them.

Declaration:

 percent: number;

Some things I have tried.

1:
this.percent = Math.round(this.percent * 1e2) / 1e2;

2:
this.percent = this.percent.toFixed(2); // Throws error cant assign string to num because to fixed returns string

3:
const percentString = this.percent.toString() + '.00';
this.percent = parseFloat(percentString) // Strips 00 (Tried this to just add zeros to whole number as test [will be making it more dynamic])

4:
this.percent = Math.round(this.percent * 100) / 100;

5: (This whole function from another SOF)

  addZeroes(num) {
// Convert input string to a number and store as a variable.
    let value = Number(num).toString();
// Split the input string into two arrays containing integers/decimals
    const res = num.split('.');
// If there is no decimal point or only one decimal place found.
    if (res.length === 1 || res[1].length < 3) {
// Set the number to two decimal places
      value = parseFloat(value).toFixed(2);
    }
// Return updated or original number.
    return value;
  }

and then

this.percent = parseFloat(this.addZeroes(this.percent));

6:
this.percent = parseFloat(this.percent).toFixed(2); // Error inside parseFloat: TS2345: Argument of type 'number' is not assignable to parameter of type 'string'

7:
this.percent = parseFloat(this.percent.toString()).toFixed(2); // Throws error on this.percent assignment: TS2322: Type 'string' is not assignable to type 'number'

8:
this.percent = Number(this.percent).toFixed(2); // Error on assignment: TS2322: Type 'string' is not assignable to type 'number'.

HTML:

  <mat-form-field>
    <input
      matInput
      [numbers]="'.'"
      type="text"
      maxlength="5"
      [placeholder]="'Percent'"
      [(ngModel)]="percent"
      (change)="updateDollarAmountNew()"
      numbers
      name="percent">
  </mat-form-field>

I have also tried piping on front end but have problems with that as well.

[(ngModel)]="p.percent | number : '1.2-2'" // Error: ng: The pipe '' could not be found

[(ngModel)]="{{percent | number : '1.2-2'}}" // Error: unexpected token '}}'

[(ngModel)]={{percent | number : '1.2-2'}} // Error: Attribute number is not allowed here

[(ngModel)]={{percent | number : 2}} // Error: : expected

// And so on...

Thanks for your tips and help!

David La Grange
  • 383
  • 1
  • 6
  • 14
  • try `[(ngModel)]="percent | number : '1.2-2'"`. Since you are passing a value into an input (in this case percent into ngModel), you don't need the `{{}}` – rhavelka Mar 15 '19 at 16:47
  • I can reference this from [here](https://stackoverflow.com/questions/6134039/format-number-to-always-show-2-decimal-places) – Sam Arul Raj T Oct 25 '22 at 11:49

2 Answers2

13

You've done all the work, but just haven't put the right bits together. Parsing the float works, and toFixed(2) was correctly returning a string with 2 decimal places, you just need to use them together:

parseFloat(input).toFixed(2)

Richard
  • 951
  • 7
  • 14
  • Thanks for answering. See update about errors received. – David La Grange Mar 15 '19 at 14:44
  • 1
    Try just using `Number()` rather than `parseFloat()`. It sounds like you've got your types confused somewhere, as your compiler thinks it's a number, but your browser thinks it's a string – Richard Mar 15 '19 at 14:46
  • Shoot nope. Added html and declaration details. – David La Grange Mar 15 '19 at 14:55
  • This answer is advocating an anti-pattern approach. Wherever possible you should always try to implement presentation using the HTML template - thats what it is for - and seperation of concerns is important :) https://medium.freecodecamp.org/best-practices-for-a-clean-and-performant-angular-application-288e7b39eb6f – Steve Land Mar 15 '19 at 15:57
11

Treating it as a number and formatting in the view is the correct approach.

However, you're mixing up binding & formatting, for example: [(ngModel)]="{{percent | number : '1.2-2'}}" is (very roughly!) equivalent to saying in english: bind my model to the string interpolation of...my model.

Try:

<div>{{percent | number : '1.2-2'}}</div>

There are good examples of number pipe usage in the docs: https://angular.io/api/common/DecimalPipe#example

Steve Land
  • 4,852
  • 2
  • 17
  • 36
  • Well that is a step forward! That does work but I do need this format to be bound to the input as shown in the HTML above. Can you think of a good way to do this? – David La Grange Mar 15 '19 at 16:13
  • @DavidLaGrange To achieve that with an input field you'll need to write yourself a directive. Here's one that achieves a similar effect - you'll need to adjust it to suit your use case: https://stackblitz.com/edit/angular-u2vcwn?file=app%2Fcurrency-input-mask%2Fcurrency-input-mask.directive.ts – Steve Land Mar 18 '19 at 10:55