Contents

Why I Prefer to Use the Parameter Object Pattern in Angular Pipes

Why I Prefer to Use the Parameter Object Pattern in Angular Pipes

Rather than directly passing around parameters.

https://cdn-images-1.medium.com/max/800/1*mNRmZb0AOl9FUYCfp-X6AA.jpeg

Source: Unsplash

I like to use the parameter object pattern to minimize the number of arguments that I need to pass around to a function or method. I first learned about this pattern fromClean Code: A Handbook of Agile Software Craftsmanship. Clean Code even suggests that three or more parameters are too much for a function.

So it makes sense to encapsulate these parameters into an object before passing them to a function if there are more than three.

In this post, I will start by dissecting how we would normally implement an Angular pipe which is passing the parameters directly. We will then go through how we would implement it using the parameter object pattern.


An Example — Fighter Profile Pipe

We will use this example in the entire blog post. Given an MMA fighter profile with the following data, for example:

  • Name: Khabib Nurmagomedov
  • Weight: 155 lbs
  • Height: 158 cm
  • Wins: 29
  • Losses: 0

We are implementing an angular pipe that will format the above data into this one-line text:

Khabib Nurmagomedov, 155 lbs, 178 cm, 29 wins, 0 losses

I will call this pipe —fighterProfile. Let’s go through the examples.

Using parameters

Let’s start with our pipe’s transform method if we are using parameters. We have thenameas our first parameter, and the succeeding parameters will be theweight,height,wins, andlosses. Theformatmethods append a string to the numeric parameters likeheight,weight,wins, andlosses.

View Live Demo

Fighter profile pipe on Stackblitz by the author.

Single fighter profile

To use thefighterProfilepipe above, we will pass the parameters directly to our template like the following.

{{ 'Khabib Nurmagomedov' | fighterProfile: 29: 0: 155: 178 }}

The output will be:

Khabib Nurmagomedov, 155 lbs, 178 cm, 29 wins, 0 losses

An array of fighter profiles

Now let’s inspect a more realistic example, an array of fighter profiles like below:

View Live Demo

An array of fighter profiles on Stackblitz by the author.

I kept thelegacyFighterProfilesin a separate file in my sample code. So we need to assign it to our component’s property first.

this.legacyFightersList = legacyFighterProfiles;

We will iterate throughlegacyFighterProfilesListand apply the pipe for each fighter profile.

<ul> <li *ngFor="let fighter of legacyFightersList"> {{ fighter.bio.name | fighterProfile: fighter.mmaRecord.wins: fighter.mmaRecord.losses: fighter.bio.weight: fighter.bio.height }} </li></ul>

The above code should output:

  • Conor McGregor, 22 wins, 6 losses, 155 lbs, 175 cm
  • Khabib, 29 wins, 0 losses, 155 lbs, 178 cm
  • Dustin Poirier, 28 wins, 6 losses, 155 lbs, 175 cm

You can find the full running example in StackBlitz:

View Live Demo

Running code on Stackblitz by the author.

Parameter Object Pattern Using an Interface

If we want to use the parameter object pattern, we will have to pass the entire fighter profile as a single parameter. Unlike in our previous example, where each fighter profile attribute is its parameter. We will start by declaring ourFighterProfileinterface.

export interface FighterProfile { name: string; weight: number; height: number; wins: number; losses: number;}

Thetransformmethod in our pipe class will take a singlefighterProfileparameter and return the formatted string. The format methods append a string to ourfighterProfileinterface’s numeric fields.

View Live Demo

Fighter profile pipe on StackBlitz by the author.

Single fighter profile

To use thisfighterProfilepipe, we can initialize a property with theFighterProfiletype.

this.singleProfile = { name: 'Khabib Nurmagomedov', wins: 29, losses: 0, weight: 155, height: 178};

In our template, we will pass thesingleProfileproperty as a single parameter to ourfighterProfilepipe.

{{ singleProfile | fighterProfile }}

Our output will be:

Khabib Nurmagomedov, 155 lbs, 178 cm, 29 wins, 0 losses

An array of fighter profiles

We will use the same array from our previous example.

View Live Demo

An array of fighter profiles on StackBlitz by the author.

Let’s start by creating a method to convert the fighter profile fields into theFighterProfiletype. The method will accept onelegacyFighterProfileparameter. It will use some of thelegacyFighterProfilefields to return theFighterProfiletype.

fighterProfile(legacyFighterProfile: LegacyFighterProfile): FighterProfile { return { name: legacyFighterProfile.bio.name, weight: legacyFighterProfile.bio.weight, height: legacyFighterProfile.bio.height, wins: legacyFighterProfile.mmaRecord.wins, losses: legacyFighterProfile.mmaRecord.losses } as FighterProfile;}

To use this pipe in our template, assign thelegacyFighterProfilesvalue to our component’slegacyFightersListproperty.

this.legacyFightersList = legacyFighterProfiles;

Then iterate through the list in our template. Apply the pipe to eachfighterProfilearray element.

<ul> <li *ngFor="let fighter of legacyFightersList"> {{ fighterProfile(fighter) | fighterProfile}} </li></ul>

This is more readable than using parameters. However, due tohow Angular change detection works, this will unnecessarily call thefighterProfile()method many times than it should be*.*

A better solution is to get rid of thefighterProfile()method and declare theFighterProfileobjectdirectly in the template. We won’t need extra calls to the component’s method:

<ul> <li *ngFor="let fighter of legacyFightersList"> {{ { name: fighter.bio.name, wins: fighter.mmaRecord.wins, losses: fighter.mmaRecord.losses, weight: fighter.bio.weight, height: fighter.bio.height } | fighterProfile }} </li></ul>

You can find the full running example in StackBlitz:

View Live Demo

Running code on Stackblitz by the author.


Conclusion

After we got rid of ourfighterProfile()method in our example, it might look like we are implementing the parameter object pattern the same way as using parameters in our template.

However, the parameter object pattern implementation is slightly more readable. Because we are clear as to what parameters we are passing to our pipe i.e. we know if we are passingweight,height,wins, andlossesvalues.

{ name: fighter.bio.name, wins: fighter.mmaRecord.wins, losses: fighter.mmaRecord.losses, weight: fighter.bio.weight, height: fighter.bio.height } | fighterProfile

If you compare that with passing parameters directly. It feels unnaturalto pass these many parameters with colons in between. Not as easy to read as the previous example.

fighter.bio.name | fighterProfile: fighter.mmaRecord.wins: fighter.mmaRecord.losses: fighter.bio.weight: fighter.bio.height

Thanks for reading.