Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angular - custom pure pipe as singleton instance?

Tags:

angular

pipe

Is it possible to have only one(!) instance of a custom pure pipe in an Angular project?

It seems to me that Angular is creating for every different component in the project a new instance of my custom pipe. It calls the constructor of my pipe for every different component I am using the pipe in.

Its about the cachability. I would love to have throughout my different components using that one pipe in their template only one instance of my custom pure pipe!

like image 625
Dachstein Avatar asked Nov 15 '22 20:11

Dachstein


1 Answers

I have just come across this exact same issue where I wanted to use a pipe to provide temporary images while a http request was pending and then cache the result of this image in the pipe, so that next time an image with the same src is loaded it can pull straight from cache.

This is what I came up with.

@Pipe({
  name: 'examplePipe',
  pure: false,
})
export class ExamplePipe implements PipeTransform, OnDestroy {
  private subscriptions: Subscription[] = [];

  constructor(private cacheService: ExamplePipeCacheService<{ url: string }>) {}

  ngOnDestroy(): void {
    for (let s of this.subscriptions) {
      s.unsubscribe();
    }
  }

  transform(value: string, ...args: any[]) {
    if (!this.cacheService.cache[value]) {
      console.log(`>> ${value} NOT in cache << `);
      //emit one item
      const example = of(null);
      //delay output of each by an extra second
      const obs = merge(
        example.pipe(mapTo('https://via.placeholder.com/50')),
        example.pipe(mapTo('https://via.placeholder.com/150'), delay(3000)),
        example.pipe(mapTo('https://via.placeholder.com/250'), delay(4000)),
        example.pipe(mapTo(value), delay(5000))
      );

      this.subscriptions.push(
        obs.subscribe((v) => {
          console.log(`++ SET ${value} in cache ++ `);
          this.cacheService.cache[value] = { url: v };
        })
      );
    } else {
      console.log(`GET ${value} from cache`);
    }
    return this.cacheService.cache[value].url;
  }
}

@Injectable({
  providedIn: 'root',
})
export class ExamplePipeCacheService<Value> {
  public cache: { [index: string]: Value } = {};
}

It uses a singleton service to store the cache so then whenever / wherever ExamplePipe is used, it will always pull its cache from the same instance.

In this working example https://stackblitz.com/edit/angular-ivy-5wwbc2?file=src/app/app.component.ts There are 3 images that load of which 2 are pointing to the same src url. You can see that the first time a unique image src is loaded, it is added to the cache, and then all following calls, using the src value as the cache key, attempts to drag the existing value from cache before making a new request.

Note that obs could be a http request, or any observable.

like image 120
Zze Avatar answered Dec 05 '22 15:12

Zze