Wanna see something cool? Check out Angular Spotify 🎧

Angular - Correct singular/plural form of a noun using custom pipe or NgPlural

Pluralization is a problem in its sphere. We need to always correctly define grammar in our apps based on the singular/plural value. E.g

1 stop 5 stops

Some websites use the (s). So that it is up to the reader to remove (s) or add (s) when reading it.

1 stop(s)
5 stop(s)

1. Custom Pipe

But as a front end developer who cares about the user experience, I always want to correct it for them.

At the beginning, I ended up to do the check for every word, so the code to display singular/plural will looks like:

<p>Number of stops: {{ numberOfStops }} stop {{ numberOfStops > 1 ? "s" : ""}}</p>

But more and more code like that could be error-prone, what if my team member when he is too tired for coding, he will do the check as numberOfStops >= 1. So that I wrote a simple plural pipe which takes a number as an argument and based on that to return the plural form of a word. As long as you are using this pipe, the result will be displayed as expected. No more funny check and error-prone.

By default it will append s to the end of the word. But of course, you could specify the plural form of a noun because not all of them ended with s in a plural form. Such as bus will become buses or hero will become heroes.

The code of PluralPipe looks like below, you can open the stackblitz to see it in action.

@Pipe({ name: 'plural' })
export class CustomPluralPipe implements PipeTransform {
  transform(input: number, customPluralForm: string = 's'): string {
    return input > 1 ? customPluralForm : ''
  }
}

To use it in the template, super easy.

<p>
  Number of stops: {{ numberOfStops }} stop {{ numberOfStops | plural}}
</p>

<p>
  Number of heroes: {{ numberOfHeroes }} hero {{ numberOfHeroes | plural:"es" }}
</p>

The problem becomes interesting when you have to do the plural form for some special words such as person, as it will come people in a plural form. Or child, will become children. I didn’t have the problem in a real project so what I can think of is to do a check like inputNumber > 1 ? "children": "child". In the long run, you could consider to abstract that condition to a simple pipe that takes a numeric value and two input noun and then renders it accordingly.

2. Angular NgPlural

You might not know that Angular has a built-in directive called NgPlural for this need. You can use this directive for the use case I mentioned above with my awesome custom pipe code. But I still prefer my approach in a normal use case :)) I don’t know, I just get used to it.

So basically, the NgPlural in action will look like:

<p [ngPlural]="numberOfStops">
    Number of stops: {{ numberOfStops }
    <ng-template ngPluralCase="=1">stop</ng-template>
    <ng-template ngPluralCase=">1">stops</ng-template>
</p>

Actually the above code only runs if with the first use case when the number is 1. If I increase it to 2, I got an error on the console ERROR Error: No plural message found for value "2". Anyway I will come back to this directive If I need to use it in the future, maybe with i18n need.

Update 23 May 2020

Based on Andy Bronson comments, you could try the code below for NgPlural. It should works very well. Noted that the UI will still using the plural form if the value is 0

<p [ngPlural]="numberOfStops">
    Number of stops: {{ numberOfStops }
    <ng-template ngPluralCase="=1">stop</ng-template>
    <ng-template ngPluralCase="other">stops</ng-template>
</p>

Output
0 stops
1 stop
2 stops

Working Example

Published 15 Feb 2020

Read more

 — How to export a table or an array in Angular to Excel file (xlsx)
 — Limit the number of simultaneous ajax requests
 — Angular formArray/formGroup - Validate at least one checkbox was selected
 — Restrict null and undefined via Non-Nullable-Types in TypeScript
 — Error handling in JavaScript. Synchronous vs asynchronous code