DEV Community

danielpdev
danielpdev

Posted on

Two ways of using AsyncPipe programmatically

Alt Text

What is AsyncPipe?

AsyncPipe is an angular pipe that helps you resolve an asynchronous value(like an observable or a promise) to a primitive one.

Common use cases of using AsyncPipe:

Being a pipe it's mostly used on HTML.

Do I need to subscribe/unsubscribe when I use the AsyncPipe on HTML?

No, the implementation of AsyncPipe will take care of subscribing and unsubscribing.

How can I use AsyncPipe programmatically in my custom pipes?

Here you can find two ways of using AsyncPipe in your custom pipes:

1. Providing AsyncPipe and ChangeDetectorRef to Dependancy Injection (DI) in your module

You might think that it will just work if you let Dependancy Injection (DI) do the work for you:

@Pipe({ name: "my_async_pipe" })
export class MyAsyncPipe implements PipeTransform {
  constructor(private asyncPipe: AsyncPipe) {}
  transform(value: any): any {
      return this.asyncPipe.transform(value);
  }
}
Enter fullscreen mode Exit fullscreen mode

Above code will not work and will throw an error like:

ERROR NullInjectorError: StaticInjectorError(AppModule)[ErrorAsyncPipe -> AsyncPipe]: 
  StaticInjectorError(Platform: core)[ErrorAsyncPipe -> AsyncPipe]: 
    NullInjectorError: No provider for AsyncPipe!
Enter fullscreen mode Exit fullscreen mode

Hmmm seams like AsyncPipe has not be added to providers array. Let's fix this error:

  • Go to app.module.ts
  • Add providers: [AsyncPipe]
  • reload
ERROR NullInjectorError: StaticInjectorError(AppModule)[AsyncPipe -> ChangeDetectorRef]: 
  StaticInjectorError(Platform: core)[AsyncPipe -> ChangeDetectorRef]: 
    NullInjectorError: No provider for ChangeDetectorRef!
Enter fullscreen mode Exit fullscreen mode

Another injector not provided, let's repeat the steps above, but this time add ChangeDetectorRef as Provider:

  • Go to app.module.ts
  • This time add providers: [AsyncPipe, ChangeDetectorRef as Provider]
  • reload

Success, AsyncPipe has been successfully provided for your custom Pipe.

2. Creating a new AsyncPipe instance

@Pipe({ name: "cdr_async_pipe" })
export class CdrAsyncPipe implements PipeTransform {
  private asyncPipe: AsyncPipe;
  constructor(private cdr: ChangeDetectorRef) {
    this.asyncPipe = new AsyncPipe(this.cdr);
  }
  transform(value: any): any {
    return this.asyncPipe.transform(value);
  }

  ngOnDestroy() {
    this.asyncPipe.ngOnDestroy();
  }
}
Enter fullscreen mode Exit fullscreen mode

Using:

 constructor(private cdr: ChangeDetectorRef) {
    this.asyncPipe = new AsyncPipe(this.cdr);
  }
Enter fullscreen mode Exit fullscreen mode

The code above creates an instance of AsyncPipe by passing the ChangeDetectorRef as argument.

If you do create your own AsyncPipe manually then please don't forget to call ngOnDestroy() lifecycle method.

Which is the best way to use AsyncPipe programmatically in your custom pipes?

In my opinion the second one is the best because DI will not create two instances of ChangeDetectionRef thus no errors like this._ref.markForChek() is not a function will be thrown as ChangeDetectionRef will be created with one of the classes that extends it.

In the next articles we will look at why when we declare ChangeDetectionRef as a provider we get the error this._ref.markForChek() is not a function.

Article first posted on danielpdev.io

Live example on codesandbox

Follow me on twitter

Top comments (2)

Collapse
 
mechasle profile image
MEChasle

Hi,
On your example, I try title$ = of("danielpdev.ioz").pipe(delay(2000)); to simulate server return.
I do not get "danielpdev.ioz" but only null instead.
What I miss ?
Thanks

Collapse
 
mechasle profile image
MEChasle

I found it.
You have to declare @Pipe({ name: "cdr_async_pipe", pure: false }).