Angular Pipes

Recently while building a new feature I found that moving logic out of the the .ts file into a pipe would make it far more re-usable in other components. The progression of this piece of work went under several revisions to reduce coupling and increase cohesion.

The initial logic looked something like this and was used to set the value of a public member ‘displayInfo‘ at ngOnChanges by calling getDisplayInfo

The value of displayInfo was then displayed in the .html file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
some-example.components.ts

public getDisplayInfo (obj1: CustomType1,
obj2: CustomType2,
obj3: CustomType3): string {
let details = [];
if (obj1) {
let { prop1, prop2 } = obj1;
details.push(`${prop1} with some text and ${prop2}.`);
}

// other simliar logic using obj2 and obj3

return details.join(' ');
}
1
2
3
some-example.components.html

{{ displayInfo }}

This worked however could be made better with a pipe as follows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
display-info.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';
// other type imports

@Pipe({
name: 'sweetPrefixDisplayInfo'
})
export class DisplayInfo Pipe implements PipeTransform {
public transform (obj1: CustomType1, obj2: CustomType2,
obj3: CustomType3): string {

// exact logic from getDisplayInfo in 'some-example.components.ts'

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
display-info.module.ts

import { NgModule } from '@angular/core';
import { PropertySearchCardDetailsPipe } from './display-info.pipe';

@NgModule({
declarations: [
DisplayInfoPipe
],
exports: [
DisplayInfoPipe
]
})
export class DisplayInfoPipeModule { }

The pipe was then imported as ‘DisplayInfoPipeModule’ into the module for my component in its module file some-example.module.ts This was then called in some-example.components.ts as follows

1
2
3
some-example.components.html

{{ obj1 | sweetPrefixDisplayInfo:obj2:obj3 }}

In my opinion this not only looks awkward but is not really how a pipe is intended to be used. It really should be seen as ‘hey sweetPrefixDisplayInfo take this value and format it’ the additional parameters could be to include additional formatting.

Example:

1
{{ obj1.SomeDate | sweetPrefixDisplayInfo:includeTime }}

However in my case we were creating a sentence string from three complex objects with rules to include parts of the sentence should valid objects be passed, these are the obj1, obj2 and obj3 objects.

As obj1, obj2 and obj3 are complex objects the pipe would not make use of all their properties, this is a code smell which is violating single responsibility and interface segregation principles.

The cleaner solution I feel would be to still use the pipe and introduce a new object specific to its function.

1
2
3
4
5
6
7
export class DisplayInfoData {
public somePropWeCareAbout1: string;
public somePropWeCareAbout2: string;
public somePropWeCareAbout3: string;

// ect
}

Then set a public property fooDisplayInfo in some-example.components.ts to the value of an instance of the type DisplayInfoData with the properties mapped from obj1, obj2 and obj3

Then call the pipe as:

1
{{ fooDisplayInfo | sweetPrefixDisplayInfo }}

Reference