Is it really better to use async-pipe instead of subscribe() in this particular situation?

Viewed 164

In my component I get an Observable as an @Input().

If the value of the Observable changes, I have to expand/collapse an ng-bootstrap accordion. So I don't really need the Observable's value in the template.

This is the code, I already have:

Component:

@Input() urlParam$!: Observable<string>;

@ViewChild('acc') accordion!: NgbAccordion;

urlParamChange$!: Observable<string>;

ngAfterViewInit(): void {
  this.urlParamChange$ = this.urlParam$.pipe(
    tap(urlParam => this.synchronizeAccordion(urlParam))
  );
}

synchronizeAccordion(urlParam: string): void {
// do something with this.accordion
}

And in the template I subscribe via async pipe to the newly created urlParamChange$:

<ng-container *ngIf="(urlParamChange$ | async)"></ng-container>

<ngb-accordion #acc="ngbAccordion">
 ....
</ngb-accordion>

Since I don't really need the value of the urlParamChange$, I put it in an own ng-container, so that is completely independent, but nevertheless the method for synchronizing the accordion will be called.

I think, that the above implementation isn't that nice. You split the action into two different files (the tap in the component is needed + additionally the async-pipe in the template).

Wouldn't it be even nicer in the following way? Instead of subscribing with the help of the async-pipe, I will subscribe() directly in the ngAfterViewInit() instead. In that way I can remove the additional variable urlParamChange$ and the implementation is all in the ngAfterViewInit() method.

Component:

@Input() urlParam$!: Observable<string>;

@ViewChild('acc') accordion!: NgbAccordion;

ngAfterViewInit(): void {
  this.urlParam$.subscribe(urlParam => this.synchronizeAccordion(urlParam));
}

synchronizeAccordion(urlParam: string): void {
// do something with this.accordion
}

And in the template the additional ng-container gets removed:

<ngb-accordion #acc="ngbAccordion">
 ....
</ngb-accordion>

In my opinion both implementations should work exactly in the same way. According to Best Practises, the first solution should be prefered, I would think. But the second one is cleaner IMO.

So what is the best way for implementing such a use case?

2 Answers

I would suggest you go with approach 2. Through your own admissions it seems cleaner and easier to follow.

Imagine someone comes along in the future and they see a random ng-container with an async binding but no content. They may well decide to remove it assuming it is no longer used, in which case your entire tap operation will no longer run & no errors will be thrown to alert you to this.

Whereas someone reading through approach 2 can very easily see what is happening and the results of modifying the subscription are apparent.

There is nothing wrong with unsubscribing manually, you just need to make sure you do it!

Why do you call first solution "best practices"? In your case there is no sense in it, use #2.

Related