Angular 4 - how to render 2 decimal places for type='input'

rmcsharry picture rmcsharry · Aug 23, 2017 · Viewed 33k times · Source

This question is about restricting/validating the input when the user enters data into an input of type number.

The issue I have is that when the model first loads, any numbers that are integers or 1dp, get rendered with only 1dp. eg 40 or 40.0 both show as 40.0 (not as 40.00).

I have added this code so that after a user types a new value, it shows with 2dp:

in the template file:

(change)="setTwoNumberDecimal($event)"

in the component file:

  setTwoNumberDecimal($event) {
    $event.target.value = parseFloat($event.target.value).toFixed(2);
  }

This works to show 2dp after the user changes the value.

I tried to write a directive that should format to 2dp when the data initially loads, but although the directive does fire, it does not change the value to 2dp.

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

@Directive ({
  selector: '[fbDecimalFormat]'
})

export class DecimalFormatDirective {

  constructor(private el: ElementRef,
              private renderer: Renderer2) {
    //renderer.setAttribute(el, 'value', parseFloat(el.nativeElement.value).toFixed(2));
    this.el.nativeElement.value = parseFloat(this.el.nativeElement.value).toFixed(2);
  }

}

FYI the commented render line did not work (error: setAttribute not recognised) - it was an attempt to be platform independent.

So the question is: How can I get the input to render its initial value with 2dps?

Answer

rmcsharry picture rmcsharry · Aug 23, 2017

Template Forms

The solution to this was to use the built-in DecimalPipe (weirdly called number) and not use two-way binding for the model value - ie. from [(ngModel)] to [ngModel] and (ngModelChange)

    <input type="number" step="0.01" 
            (change)="setTwoNumberDecimal($event)"
            (ngModelChange)="item.value=$event"
            [ngModelOptions]="{updateOn: 'blur'}"
            [ngModel]="setting.decimal_value | number:'1.2-2'">

See this question for more info on splitting the [()] binding.

Notice also that the update fires only when the user leaves the control (blur) otherwise every time they type something it will update, which will be a frustrating UX (thanks to @Mihail's comment).

Reactive Forms

In response to the question in the comments: "Could this be used with reactive forms as well?"

The answer is:

No, using pipes with reactive forms is more complicated. You could use the pipe merely to display the value, like

 <p>{{form.get('name').value | myPipe}}</p> 

but I think the correct way is to use value accessor. More info here.