When working with Angular, surely you have heard the one magic rule for your templates: Don’t call any methods.

And there is good reason: The way Angular Change Detection works, your method will be called in every CD cycle. This can lead to massive performance implications, or even weird, unforseen effects, e.g. when dynamically creating a random value.

The component method is recomputed every time the mousemove-Event fires.

Why We Need Memoization in Angular Templates

Let’s be honest: The example above is not how you would write any real-life code. Anyway, when moving from small SPA landing pages to real business applications, templates are getting more and more complicated. I often felt the urge to just call a hasPermissions('READ') method instead of building observables for every possible permission. But, of course, the same logic applies: This check would run in every single cycle.

When I built a file-explorer-like tree in one of my projects, I had to check the current users permissions for every object in that tree. Anyway, I was not able to wrap every object in it’s own component. I just wanted a way to build an observable of the users permissions on the fly.

Usually, that’s where Angular’s Pipes come to the rescue. Pipes are a simple way to define functions that can be run in the template. They are used to transform an object to a nice “displayed value”, like a currency, a date or something similar. Because the same input always leads to the same output, such pipes are called pure. Pure pipes will be called exactly once for each input, then the output value is automatically memoized.

<div [disabled]="current | hasPermission:'READ'">
  {{ current.name }}
</div>

While pipes are a very useful feature, they’re not always the simplest solution. You define them in their own class, they don’t have access to the components functionality and state. Also, back then, they had to live in some module. On the other hand, to keep the code structured, I wanted to have the tree-related functionality in my tree component.

The thought that this is not only a me-issue comes from a quick npm search. The most promising and used, ngx-pipe-function wants to solve the same problem, but the “inverse syntax” is kind of weird to me. Also, stateful methods must be explicitly bound to the components context. Because I also didn’t really like the other existing approaches, I decided to build my own.

How ngx-function-expression Saves the Day

ngx-function-expression makes it possible to use the power of Angular pipes, while calling your objects and components methods from the template. The package provides a generic fnApply-pipe used to call any given function, as well as some syntactic sugar on top. To replace the above example, you could write the following:

<div [disabled]="hasPermission | fnApply:[current, 'READ']">
  {{ current.name }}
</div>

As you can see, the syntax is quite easy. Instead of calling your custom pipe, you’re calling the fnApply-pipe on the components method, passing the arguments as parameters to the pipe. Every function will be treated as pure, so it is only re-evaluated when the inputs change. Also, fnApply automatically executes the method in the context of that component, so you never use the wrong this. 🤯

What’s the benefit? In the hasPermission method, I can now use all the services injected into my component as well as local state. Everything I use in the template is now inside that component class.

Also, because we’re using TypeScript, the parameters current and 'READ' are automatically type-checked as parameters to hasPermission. Nice! 🤓

Get Started: Using ngx-function-expression

Here’s how to get started with ngx-function-expression.

  1. Install the package:
npm install ngx-function-expression
  1. Import the FunctionExpressionModule in your app
import { NgModule } from '@angular/core';
import { FunctionExpressionModule } from 'ngx-function-expression';

@NgModule({
  declarations: [...],
  imports: [..., FunctionExpressionModule],
  providers: [...]
})
export class AppModule { }

Alternatively, since Angular 17, you can use the pipe standalone. Just import FnApplyPipe directly to the modules or components where you need it.

  1. Use the fnApply directive in your template:
<p>The meaning of life is {{ calculateMeaningOfLife | fnApply:[1, 2] }}</p>

calculateMeaningOfLife(in1: number, in2: number) {
  // Your complex function logic here
  return 42;
}

Now, the calculateMeaningOfLife function will only be called once, no matter how many times your template re-renders.

Open Source Love: Let’s Discuss and Contribute

Remember, ngx-function-expression is an open-source project, and open source thrives on community. So, if you have questions, suggestions, or just want to chat about the best way to memoize a sloth’s nap schedule, feel free to start a discussion or contribute to the project on GitHub! ❤️

Together, let’s keep our Angular applications performant and make memoization the new “hello world” for all template functions!


Deep Dive: Syntactic Sugar and other Options

Still here? Awesome! In this part, I will show you some more options you have when using ngx-function-expression.

Zero- and Single-Argument-Functions

When the method you call only has a single argument, you can drop the array braces. Just pass a single argument in that case. When you have no arguments at all, you can drop all the parameters.

<p>My name is {{ singleArgumentMethod | fnApply:'Nico' }}</p>

<p>My favorite emoji is {{ zeroArgumentMethod | fnApply }}</p>

Binding the Context

One of the best parts of using ngx-function-expression is that you never have to worry about in which context your method is executed. It always takes the component as your this-pointer. However, if necessary, it’s still easy to override that by using the second parameter to the fnApply-pipe.

<p>Who am I? {{ changeMyContext | fnApply:[...params]:contextObject }}</p>

When calling an objects method, it’s even simpler to use the fnMethod pipe also provided in the package. fnMethod takes the name of an objects method and the parameters and automatically keeps this bound to the object itself.

<p>My best friend is {{ you | fnMethod:'getName':[...params] }}</p>

Interoperability with Observables & Signals

To solve the tree-with-permissions-issue outlined above, it’s also crucial that a users permissions can change at any time. Thats why my method outputs an observable (once!) which then emits updates about the permissions. Because everything you’ve seen so far is normal Angular syntax, you can use Observables or signals just like you normally would, both in parameters and as the output value.

<!-- re-evaluate createCountdown(duration) whenever duration$ changes -->
<!-- after evaluating, subscribe to the returned countdown observable -->
<p>It's the final Countdown: {{ createCountdown | fnApply:[(duration$ | async)] | async }}</p>

<!-- re-evaluate getColor(col) whenever signal changes -->
<!-- then, display the returned signal -->
<p>Traffic Light: {{ (getColor | fnApply:[signal()])() }}</p>

⚠️ As you can see, such constructs are getting complicated to read and understand very fast. Try to use computed signals and the .pipe()-operator whenever possible.

There is More to Explore…

If you want to read about more examples or ask your own questions, head over to the full README on GitHub.