Intro

If you've worked with Angular on a more complicated project, you'll almost certainly start writing component methods to change or alter data that you want to display in the component. If you are unfamiliar with the Angular way of doing things, you can simply utilize the functions you defined in the HTML file and it will work! So far, everything has gone well, hasn't it?

Unfortunately, this is not the case. You'll learn why this is a terrible practice and how to fix the problem in a clean, maintainable manner in this lesson.

Prerequisite

You need to know about angular Lifecycle hooks to understand why calling functions in HTML is undesirable and would severely slow down your application if you abuse it.

In a word, these are a set of functions — precisely, 15 methods — that are run each time a component is created, updated, or deleted; it's how Angular detects changes in the application.

In a change detection process that runs after every DOM event: every keystroke, mouse motion, timer tick, and server response, Angular searches for changes to data-bound variables.

Problem

So say you have a good application where you structure your components in a hierarchical fashion to encourage code reuse, and you have one component with ten nested components beneath it, and a function called in the least nested component. With any DOM event, this method will be called around 15 x 10 = 150 times!

Apart from slowing down your program, if any logic is included in some of the functions you call, you risk breaking it and introducing a significant source of unpredictability.

So, how do you limit the function call's scope to only when its input changes and not otherwise?


<ng-container *ngFor="let item of items">
   <tr>
      <td title="Item"
         class="text-muted">{{editOrTransformItem(item)}}:
      </td>
      <td> {{ item.name }}</td>
   </tr>
</ng-container>

To the rescue, pipes

In such cases, Angular offers a nice feature called pipes that you may utilize; the official docs can be found here. There will be practically nothing new to learn if you are already familiar with Linux pipes. Continue reading if you haven't already.

In a nutshell, pipes allow you to run transformation code on input and return the output, just like any other function. However, Angular only executes the pipe when it detects a change in the input value or reference, not with all lifecycle hooks.

As a result, the following is a better method to write the previously described snippet:
<ng-container *ngFor="let item of items">
      <tr>
        <td title="Item"
            class="text-muted">{{ item | editOrTransformItem }}:
        </td>
        <td> {{ item.name }}</td>
      </tr>
</ng-container>
Now you could make a pipe that looks something like this:

@Pipe({
  name: 'editOrTransformItem'
})
export class EditOrTransformItemPipe implements PipeTransform {
  transform(value: string, ...args: any[]): string {
    let result = "put here your transforamtion login"
    return result;
}
This refactoring step will also assist to increase code reusability across the application, because you can now use this pipe everywhere in your app without having to write the transformation logic in each component you need to utilize, as you would with a simple JS function.

Theargsarray allows pipes to take an infinite number of arguments. Other arguments can be sent to it and accessed later in the transfom method.

Caveat

Keep in mind that the pipes' functionality is limited to simple input values like String or Number, or an object reference. This implies that if you provide an array to the pipe and subsequently update it by adding or deleting items, the pipe will not notice. This is the same object's reference for the pipe.

You have two options for resolving this issue:

1- With each array edit, return a new array (new reference) (recommended for performance-critic applications).

2- Make the pipe impure, and it will act as a function that will be called in all lifecycle hooks.