0

I have a numeric textbox which show percentage value and allow user to update it in Angular application.

enter image description here

            <label for="fees">Fees %</label>
            <div class="input-group">
                <input type="number" class="form-control" id="fees" name="fees"
                 [(ngModel)]=feesPercentage">
                <div class="input-group-prepend">
                    <span class="input-group-text" id="FeesSign">%</span>
                </div>
            </div>

feesPercentage is stored as a decimal value in the model (e.g. 0.15). Is there a pipe available to show the value as percentage (e.g. 15) in the textbox while still keeping the value as decimal in the model. E.g. If user updates it from 15 to 16, I would like to change the model value to 0.16. I don't need to show "%" sign in the textbox.

developer
  • 1,401
  • 4
  • 28
  • 73
  • 1
    Use getter/setter methods on the model? `get feesPercentageValue() { return this.feesPercentage * 100; } set feesPercentageValue(value) { this.feesPercentage = value / 100; }`. – Heretic Monkey Aug 04 '21 at 23:07
  • Thanks for your quick reply. It would work but I have many such fields so looking for a simple solution (possibly in HTML only). – developer Aug 04 '21 at 23:16
  • The highest-voted answer to the proposed duplicate (the comment beginning with "Does this answer your question?") uses code directly in the HTML... – Heretic Monkey Aug 04 '21 at 23:18

1 Answers1

3

The pipe is the Decimal pipe:

https://angular.io/api/common/DecimalPipe

something like this for normal usage:

{{ (feesPercentage * 100) | number: '1:0-0' }}

the 1 means display at least 1 digit before the decimal point.

the first 0 means display a minimum of 0 digits after the decimal point.

the second 0 means display a maximum of 0 digits after the decimal point.

USING A GETTER

But I think in your case a pipe might not be appropriate. Instead try using a getter like so:

In your .ts file:

get feesPercentageAsInteger(): number {
  return Math.round(this.feesPercentage * 100)
}

then in your HTML:

[ngModel]="feesPercentageAsInteger" 
(ngModelChange)="updateFeesPercentage($event)"

then again in your .ts file:

updateFeesPercentage(value) {
  this.feesPercentage = parseInt(value, 10) / 100; // using parseInt here because I think value will be a string (but parseInt will also work if its a number)
}

USING A CUSTOM PIPE:

Another possible solution. Instead of using a getter this MIGHT work:

[ngModel]="(feesPercentage * 100) | round" 
(ngModelChange)="updateFeesPercentage($event)"

then again in your .ts file:

updateFeesPercentage(value) {
  this.feesPercentage = parseInt(value, 10) / 100;
}

you will notice here that I've used a round custom pipe. The reason for this is because you want an integer for your textbox. So you would need to create a custom pipe to do the rounding (unless the decimal number always has no more than 2dp) which should be simple for you to do. It would literally take a number and use Math.round to do the rounding and return the outcome (use ... ng generate pipe ... at the CLI to create the pipe). Any problems then let me know. It might even work without the custom pipe but I doubt it.

The only problem you might face is a loss of precision when dividing the number in the input by 100 which will give you a decimal with 2dp (when you original number may have contained more than 2dp). If loss of precision is a concern here then consider tweaking the updateFeesPercentage function to get the value of additional dps from feesPercentage before setting it (and appending them to it when setting it).

NB: The loss of precision concerns also apply when using the GETTER approach.

NB: Again if loss of precision is a concern then using Math.floor instead of Math.round in your custom round pipe will ensure that you do not lose precision.

NB: Would using the step attribute help you out here? Probably not but just a heads up ... https://stackoverflow.com/a/22641142/1205871

danday74
  • 52,471
  • 49
  • 232
  • 283